home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #2 / Monster Media No. 2 (Monster Media)(1994).ISO / utils1 / 2m21src.zip / 2MSYS.ASM < prev    next >
Assembly Source File  |  1994-05-31  |  94KB  |  2,248 lines

  1.  
  2. ;┌───────────────────────────────────────────────────────────────────┐
  3. ;│                                                                   │
  4. ;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▄  ▒▒▒▒▄      ▒▒▒▒▄                                │
  5. ;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█  ▒▒▒▒▒▄    ▒▒▒▒▒█                                │
  6. ;│  ▀▀▀▀▀▀▀▀▀▀▒▒▒▒█  ▒▒▒▒▒▒▄  ▒▒▒▒▒▒█                                │
  7. ;│            ▒▒▒▒█  ▒▒▒▒▒▒▒▄▒▒▒▒▒▒▒█                                │
  8. ;│            ▒▒▒▒█  ▒▒▒▒█▒▒▒▒▒█▒▒▒▒█                                │
  9. ;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█  ▒▒▒▒█ ▒▒▒█▀▒▒▒▒█    ▒▒▒▒▒▒▒▒▒▒▄         ▒▒▄     │
  10. ;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█  ▒▒▒▒█  ▒█▀ ▒▒▒▒█     ▀▀▀▀▀▀▀▒▒█       ▒▒▒▒█     │
  11. ;│ ▒▒▒▒█▀▀▀▀▀▀▀▀▀▀▀  ▒▒▒▒█   ▀  ▒▒▒▒█            ▒▒█     ▒▒▄▀▒▒█     │
  12. ;│ ▒▒▒▒█             ▒▒▒▒█      ▒▒▒▒█    ▒▒▒▒▒▒▒▒▒▒█      ▀▀ ▒▒█     │
  13. ;│ ▒▒▒▒█             ▒▒▒▒█      ▒▒▒▒█    ▒▒█▀▀▀▀▀▀▀▀         ▒▒█     │
  14. ;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▄  ▒▒▒▒█      ▒▒▒▒█    ▒▒█                 ▒▒█     │
  15. ;│ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒█  ▒▒▒▒█      ▒▒▒▒█    ▒▒▒▒▒▒▒▒▒▒▄ ▒▒▄ ▒▒▒▒▒▒▒▒▒▒▄ │
  16. ;│  ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀   ▀▀▀▀       ▀▀▀▀     ▀▀▀▀▀▀▀▀▀▀  ▀▀  ▀▀▀▀▀▀▀▀▀▀ │
  17. ;│                                                                   │
  18. ;│         2M 2.1  -  (C) Mayo 1994  Ciriaco García de Celis.        │
  19. ;│                                                                   │
  20. ;│      SOPORTE PARA DISQUETES CON MAYOR CAPACIDAD DE LA NORMAL      │
  21. ;│                    CREADOS POR LA UTILIDAD 2MF                    │
  22. ;│                                                                   │
  23. ;│       * * *   Versión instalable desde el CONFIG.SYS  * * *       │
  24. ;│                                                                   │
  25. ;│ - Sólo para AT y superiores con controladora y unidades de alta.  │
  26. ;│ - Programación directa del controlador de disquetes y del DMA.    │
  27. ;│ - Soporte para sectores de más de 512 bytes (economizar GAPs).    │
  28. ;│ - Mezcla de sectores de distinto tamaño en la misma pista.        │
  29. ;│ - Ruptura del límite habitual de 80 pistas (incluido 360K).       │
  30. ;│ - Emulación de sectores de 512 bytes en INT 13h.                  │
  31. ;│ - Función 5 (INT 13h) reforzada para formatear los nuevos discos. │
  32. ;│ - El soporte residente opera eficazmente bajo DOS y WINDOWS 3.X   │
  33. ;│                                                                   │
  34. ;│   Emplear TASM /m5, TLINK y EXE2BIN para obtener un fichero SYS   │
  35. ;│                                                                   │
  36. ;└───────────────────────────────────────────────────────────────────┘
  37.  
  38.                .286                    ; versión para AT o superior
  39.  
  40. ; ------------ Macros de propósito general.
  41.  
  42. XPUSH          MACRO regmem            ; apilar lista de registros
  43.                  IRP rm, <regmem>
  44.                    PUSH rm
  45.                  ENDM
  46.                ENDM
  47.  
  48. XPOP           MACRO regmem            ; desapilar lista de registros
  49.                  IRP rm, <regmem>
  50.                    POP rm
  51.                  ENDM
  52.                ENDM
  53.  
  54. DELAY          MACRO                   ; estados de espera
  55.                  JMP SHORT $+2         ; para AT obsoleto
  56.                  JMP SHORT $+2
  57.                ENDM
  58.  
  59. PMICRO         MACRO                   ; retardo de aprox. 15,09 µs
  60.                LOCAL pmicro_iter       ; (exactamente 18/1193180 sg.)
  61. pmicro_iter:   DELAY
  62.                IN    AL,61h            ; Esta macro puede ejecutarse
  63.                AND   AL,10h            ; repetitivamente (se apoya en
  64.                CMP   AL,AH             ; AX) para hacer retardos a
  65.                JE    pmicro_iter       ; través de la temporización
  66.                MOV   AH,AL             ; del refresco de la memoria
  67.                ENDM                    ; dinámica de los AT.
  68.  
  69. ; ------------ Estructura de datos con información para cada unidad.
  70.  
  71. info_drv       STRUC
  72. maxs           EQU   13           ; máximo 13 sectores físicos/pista
  73. tipo_drv       DB    ?            ; tipo de la disquetera (0 = no hay)
  74. control2m_flag DB    OFF          ; a ON si 2M controla la unidad
  75. cambio         DB    ON           ; a ON indica cambio de soporte
  76. version_fmt    DB    ?            ; versión del formato de disco 2M
  77. multi_io       DB    ?            ; a 0 si posible acceso multi-sector
  78. chk            DB    ?            ; a 0 si checksum del sector 0 Ok
  79. vunidad        EQU   THIS WORD
  80. vunidad0       DB    ?            ; velocidad pista 0
  81. vunidadx       DB    ?            ; velocidad demás pistas
  82. gap            DB    ?            ; GAP entre sectores (leer/escribir)
  83. sectpista      DB    ?            ; sectores lógicos por pista
  84. tabla_tsect    DB    maxs DUP (?) ; tamaños de sectores 1, 2, ..., N
  85. tam_fat        DB    ?            ; sectores/FAT en la unidad
  86.                ENDS
  87.  
  88. ; ------------ Programa.
  89.  
  90. _PRINCIPAL     SEGMENT
  91.                ASSUME CS:_PRINCIPAL, DS:_PRINCIPAL
  92.  
  93.                ORG   0
  94.  
  95. ini_residente  EQU   $
  96.  
  97.                DD    -1           ; encadenamiento con otros drivers
  98. tipo_drive     DW    8000h        ; palabra de atributo:
  99.                                   ; bit 15 a 1: dispositivo caracteres
  100.                                   ; bit 14 a 0: sin control IOCTL
  101.                DW    estrategia   ; rutina de estrategia
  102.                DW    interrupcion ; rutina de interrupción
  103.                DB    "2M$     "   ; nombre del dispositivo
  104.  
  105. estrategia     PROC  FAR
  106.                MOV   CS:pcab_pet_segm,ES
  107.                MOV   CS:pcab_pet_desp,BX
  108.                RET
  109. estrategia     ENDP
  110.  
  111. interrupcion   PROC  FAR
  112.                CALL  main  ; tras instalar: XPUSH <DS, BX> y MOV BX,??
  113. pcab_pet_segm  DW    ?
  114.                MOV   DS,BX
  115.                DB    0BBh  ; opcode de MOV BX,??
  116. pcab_pet_desp  DW    ?
  117.                MOV   WORD PTR [BX+3],8103h  ; código de error
  118.                XPOP  <BX, DS>
  119.                RET
  120. interrupcion   ENDP
  121.  
  122. ; ****************************************
  123. ; *                                      *
  124. ; *   D A T O S    R E S I D E N T E S   *
  125. ; *                                      *
  126. ; ****************************************
  127.  
  128. ; ------------ Identificación estandarizada del programa.
  129.  
  130. program_id     LABEL BYTE
  131. segmento_real  DW    0   ; segmento real donde será cargado
  132. offset_real    DW    0   ; offset real     "     "     "
  133. longitud_total DW    0   ; zona de memoria ocupada (párrafos)
  134. info_extra     DB    02h ; bits 0, 1 y 2-> 000: normal, con PSP
  135.                          ;                 001: bloque UMB XMS
  136.                          ;                 010: *.SYS
  137.                          ;                 011: *.SYS formato EXE
  138.                          ; bit 7 a 1: «extension_id» definida
  139. multiplex_id   DB    0   ; número Multiplex de este TSR
  140. vectores_id    DW    tabla_vectores
  141. extension_id   DW    0
  142.                DB    "*##*"
  143. autor_nom_ver  DB    "CiriSOFT:2M:2.1",0
  144.  
  145.                DB    2  ; número de vectores de interrupción usados
  146. tabla_vectores EQU   $
  147. vieja_i13      DB    13h           ; INT 13h
  148. ant_int13      LABEL DWORD         ; dirección original
  149. ant_int13_off  DW    0
  150. ant_int13_seg  DW    0
  151.                DB    2Fh           ; INT 2Fh
  152. ant_int2F      LABEL DWORD         ; dirección original
  153. ant_int2F_off  DW    0
  154. ant_int2F_seg  DW    0
  155.  
  156. ; ------------ Variables del programa.
  157.  
  158. info_ptr       DW    info_A       ; punteros a datos de las unidades
  159.                DW    info_B
  160. id_sistema     DB    "2M-STV"     ; identificación de disco 2M
  161. unidad         DB    ?            ; unidad física de disco en curso
  162. numsect        DW    ?            ; sectores a transferir
  163. sectini        DW    ?            ; primer sector DOS a transferir
  164. cilindro       DB    ?            ; cilindro del disco a acceder
  165. cabezal        DB    ?            ; cabezal a emplear
  166. sector         DB    ?            ; número de sector físico
  167. sector_ini     DB    ?            ; número de sector físico inicial
  168. sector_fin     DB    ?            ; número de sector físico final
  169. seccion        DB    ?            ; parte del sector físico en curso
  170. secciones      DB    ?            ; sectores lógicos a transferir
  171. tsector        DB    ?            ; LOG2 (tamaño de sector) - 7
  172. buffer         DW    buffer_io    ; puntero al buffer intermedio
  173. buf_unidad     DB    ?            ; unidad del sector en el buffer
  174. buf_cilcab     DW    ?            ; cilindro/cabezal de sector buffer
  175. buf_sector     DB    ?            ; número de sector en el buffer
  176. status         DB    ?            ; resultado de los accesos a disco
  177. fdc_result     DB    7 DUP (?)    ; bytes de resultados del FDC
  178. orden          DB    ?            ; operación F_READ/F_WRITE/F_VERIFY
  179. tab_ordenes    DB    F_READ
  180.                DB    F_WRITE
  181.                DB    F_VERIFY     ; órdenes 2, 3 y 4
  182.  
  183.                ; --- Interpretación BIOS de los bits de ST1
  184.  
  185. lista_errs     DB    4            ; 'sector not found'
  186.                DB    0
  187.                DB    10h          ; 'bad CRC'
  188.                DB    8            ; 'DMA overrun'
  189.                DB    0
  190.                DB    4            ; 'sector not found'
  191.                DB    3            ; 'write-protect error'
  192.                DB    2            ; 'address mark not found'
  193.                DB    20h          ; en otro caso: 'bad NEC'
  194.  
  195. info_A         info_drv <>        ; datos de A:
  196. info_B         info_drv <>        ; datos de B:
  197.  
  198. ; ***************************************
  199. ; *                                     *
  200. ; *   C O D I G O   R E S I D E N T E   *
  201. ; *                                     *
  202. ; ***************************************
  203.  
  204. ; ------------ Rutina de gestión de INT 2Fh.
  205.  
  206. ges_int2F      PROC  FAR
  207.                STI
  208.                CMP   AH,CS:multiplex_id
  209.                JE    preguntan
  210.                JMP   CS:ant_int2F      ; saltar al gestor de INT 2Fh
  211. preguntan:     CMP   DI,1992h
  212.                JNE   ret_no_info       ; no llama alguien del convenio
  213.                MOV   AX,ES
  214.                CMP   AX,1492h
  215.                JNE   ret_no_info       ; no llama alguien del convenio
  216.                PUSH  CS
  217.                POP   ES                ; sí llama: darle información
  218.                LEA   DI,autor_nom_ver
  219. ret_no_info:   MOV   AX,0FFFFh         ; "entrada multiplex en uso"
  220.                IRET
  221. ges_int2F      ENDP
  222.  
  223. ; ------------ Nueva rutina de gestión de INT 13h. Llama a la INT 13h
  224. ;              original o a una nueva rutina de control para la
  225. ;              lectura (AH=2), escritura (AH=3) y verificación (AH=4)
  226. ;              según el tipo de disco introducido. Ante una función de
  227. ;              formateo (AH=5) se entrega el control a la INT 13h
  228. ;              original. Se detecta un posible cambio de disco y se
  229. ;              retorna en ese caso con el correspondiente error.
  230.  
  231. ges_int13      PROC  FAR
  232.                STI
  233.                CLD
  234.                PUSHF
  235.                CMP   DL,2
  236.                JAE   ges13bios         ; no es disquetera A: ó B:
  237.                PUSH  SI
  238.                CALL  set_SI_drv
  239.                CMP   CS:[SI].tipo_drv,2  ; ¿unidad 1.2M?
  240.                JE    ges_2m
  241.                CMP   CS:[SI].tipo_drv,4  ; ¿unidad 1.44/2.88M?
  242. ges_2m:        POP   SI
  243.                JC    ges13bios         ; no es unidad de alta densidad
  244.                CMP   AH,2
  245.                JB    ges13bios         ; no Read/Write/Verify/Format
  246.                CMP   AH,5
  247.                JA    ges13bios         ; no Read/Write/Verify/Format
  248.                CALL  detecta_cambio    ; ¿cambio de disco?
  249.                JNC   sin_cambio
  250.                POPF
  251.                STC                     ; hubo cambio:
  252.                MOV   AX,600h
  253.                RET   2                 ; retornar con error
  254. sin_cambio:    CMP   AH,5
  255.                JNE   dilucida          ; no es orden de formateo
  256.                CALL  leer_lin_camb
  257.                JNZ   format_bios       ; no hay disquete en la unidad
  258.                CMP   AL,7Fh
  259.                JNE   format_bios       ; no es orden formateo de 2M
  260.                CMP   SI,"2M"
  261.                JE    format_2m         ; es orden de formateo de 2M
  262. format_bios:   CLC
  263.                CALL  set_flag_STV      ; CF = 0 -> indicar no 2M
  264. dilucida:      PUSH  SI
  265.                CALL  set_SI_drv        ; SI -> variables de la unidad
  266.                CMP   CS:[SI].control2m_flag,OFF
  267.                POP   SI
  268.                JE    ges13bios         ; la unidad la controla la BIOS
  269.                POPF
  270.                CALL  control2m         ; la controla 2M
  271.                RET   2
  272. ges13bios:     POPF
  273.                JMP   CS:ant_int13      ; saltar al gestor de INT 13h
  274.  
  275.                ; --- Función de formateo implementada por 2M. En los
  276.                ;     disquetes creados con /M todas las pistas salvo
  277.                ;     la 0 deberían ser formateadas invocando INT 13h
  278.                ;     de manera directa (con CALL) para evitar que se
  279.                ;     ejecute cierto código de WINDOWS en el modo
  280.                ;     protegido que provoca errores al formatear. Antes
  281.                ;     de formatear la primera pista física del disco se
  282.                ;     invoca la función de formateo de la INT 13h
  283.                ;     original (con un error para provocar un rápido
  284.                ;     retorno) con objeto de informar al DOS y a todos
  285.                ;     los TSR previos del cambio de soporte.
  286.  
  287. format_2m:     POPF
  288.                PUSH  DS                ; *
  289.                PUSHA                   ; **
  290.                PUSH  CS
  291.                POP   DS
  292.                MOV   unidad,DL
  293.                CALL  set_SI_drv
  294.                MOV   cilindro,CH
  295.                MOV   cabezal,DH
  296.                OR    CH,DH
  297.                JNZ   format_trx        ; no es cilindro 0 y cabezal 0
  298.                PUSHA
  299.                MOV   AL,1              ; formatear un sector (AH=5)
  300.                MOV   CH,0
  301.                MOV   DH,2              ; en cabezal 2 (incorrecto)
  302.                PUSHF
  303.                CALL  ant_int13         ; avisar al DOS del nuevo disco
  304.                CLD                     ; mantener DF=0
  305.                STC
  306.                CALL  reset_drv         ; asegurar aceleración motor
  307.                POPA
  308.                CALL  set_info          ; características nuevo soporte
  309. format_trx:    CALL  genera_info       ; tabla de información formateo
  310.                CALL  motor_ok          ; asegurar que está en marcha
  311.                CALL  seek_drv
  312.                CALL  formatea_pista
  313.                PUSHF
  314.                CLC
  315.                CALL  motor_off_cnt     ; cuenta normal detención motor
  316.                POPF
  317.                CALL  set_err
  318.                CALL  set_bios_err      ; no altera flags
  319.                POPA                    ; **
  320.                MOV   AH,status
  321.                POP   DS                ; *
  322.                RET   2
  323. ges_int13      ENDP
  324.  
  325. ; ------------ A la entrada en DL se indica la unidad y a la salida se
  326. ;              devuelve SI apuntando sus variables sin alterar flags.
  327.  
  328. set_SI_drv     PROC
  329.                PUSHF
  330.                PUSH  BX
  331.                MOV   BL,DL
  332.                MOV   BH,0
  333.                SHL   BX,1
  334.                MOV   SI,CS:[BX+OFFSET info_ptr]
  335.                POP   BX
  336.                POPF
  337.                RET
  338. set_SI_drv     ENDP
  339.  
  340. ; ------------ Si CF=1, indicar disquete 2M presente. A la
  341. ;              entrada, DL indica la unidad de disco.
  342.  
  343. set_flag_STV   PROC
  344.                PUSHA
  345.                CALL  set_SI_drv
  346.                MOV   AL,ON                       ; indicar 2M
  347.                JC    tipo_stv_ok
  348.                MOV   AL,OFF                      ; indicar no 2M
  349. tipo_stv_ok:   MOV   CS:[SI].control2m_flag,AL
  350.                POPA
  351.                RET
  352. set_flag_STV   ENDP
  353.  
  354. ; ------------ Devolver ZF=1 si cilindro y cabezal 0.
  355.  
  356. pista0?        PROC
  357.                PUSH  AX
  358.                MOV   AL,cabezal
  359.                OR    AL,cilindro
  360.                POP   AX
  361.                RET
  362. pista0?        ENDP
  363.  
  364. ; ------------ Devolver ZF=1 si la línea de cambio de disco está
  365. ;              inactiva. A la entrada, DL contiene la unidad. El
  366. ;              motor es puesto en marcha y, si no lo estaba ya, la
  367. ;              variable que indica lo que resta para detenerlo
  368. ;              es llevada a su valor normal, por lo que el disco no
  369. ;              tardará mucho en detenerse (incluso sin quizá haber
  370. ;              acelerado aún). En la práctica, invocando esta rutina
  371. ;              desde INT 13h nunca será necesario arrancar el motor
  372. ;              ya que el DOS ejecuta antes la función equivalente,
  373. ;              la 16h, que lo pone en marcha. Es simplemente una
  374. ;              medida de seguridad contra las BIOS «de marca».
  375.  
  376. leer_lin_camb  PROC
  377.                PUSHA                   ; *
  378.                PUSH  DS
  379.                PUSH  40h
  380.                POP   DS
  381.                MOV   AL,1
  382.                MOV   CL,DL
  383.                SHL   AL,CL             ; bit de motor en 0..3
  384.                TEST  DS:[3Fh],AL
  385.                JNZ   rodando           ; el motor ya está girando
  386.                CLC
  387.                CALL  motor_off_cnt     ; cuenta normal detención motor
  388. rodando:       MOV   AH,DL
  389.                SHL   AH,4
  390.                OR    AH,AL             ; AH = byte BIOS
  391.                SHL   AL,4
  392.                OR    AL,00001100b      ; modo DMA, no hacer reset
  393.                OR    AL,DL             ; AL para reg. salida digital
  394.                MOV   DX,3F2h
  395.                CLI
  396.                MOV   DS:[3Fh],AH       ; actualizar variable BIOS
  397.                OUT   DX,AL             ; arrancado motor en la unidad
  398.                ADD   DX,5
  399.                DELAY
  400.                IN    AL,DX             ; leer línea de cambio de disco
  401.                STI
  402.                TEST  AL,80h            ; ZF=0 -> cambio de disco
  403.                POP   DS
  404.                POPA                    ; *
  405.                RET
  406. leer_lin_camb  ENDP
  407.  
  408. ; ------------ Determinar si ha habido cambio de disco y, en ese caso,
  409. ;              si el nuevo disquete es de tipo 2M o no. El cambio de
  410. ;              disco se detecta leyendo la línea de cambio de disco o
  411. ;              chequeando la variable que indica si ha habido cambio
  412. ;              o no (esta variable está a ON tras instalar 2M para
  413. ;              forzar la detección del tipo de disco introducido; se
  414. ;              pone en ON también si no se logra bajar la línea de
  415. ;              cambio de disco por si fuera un soporte raro y la BIOS
  416. ;              sí lo lograra -forzando así una detección posterior-).
  417.  
  418. detecta_cambio PROC
  419.                PUSHA                   ; *
  420.                CALL  set_SI_drv        ; SI -> variables de la unidad
  421.                CMP   CS:[SI].cambio,ON ; ¿cambio de soporte?
  422.                MOV   CS:[SI].cambio,OFF
  423.                JE    hubo_cambio
  424.                CALL  leer_lin_camb     ; leer línea de cambio de disco
  425.                JNZ   hubo_cambio
  426.                POPA
  427.                CLC                     ; no hay cambio de disco
  428.                RET
  429. hubo_cambio:   CLC
  430.                CALL  set_flag_STV      ; CF = 0 -> supuesto no 2M
  431.                XPUSH <DS, ES>          ; **
  432.                MOV   BX,90h
  433.                ADD   BL,DL
  434.                PUSH  40h
  435.                POP   DS
  436.                AND   BYTE PTR [BX],255-16 ; densidad no determinada
  437.                XPUSH <CS, CS>
  438.                XPOP  <DS, ES>
  439.                MOV   unidad,DL
  440.                STC                     ; asegurar motor en marcha
  441.                CALL  reset_drv
  442.                MOV   cilindro,1
  443.                MOV   cabezal,0
  444.                CALL  seek_drv          ; bajar línea cambio de disco
  445.                DEC   cilindro
  446.                CALL  seek_drv
  447.                CLC
  448.                CALL  motor_off_cnt     ; cuenta normal detención motor
  449.                CALL  leer_lin_camb     ; ¿bajada línea cambio disco?
  450.                JZ    disco_dentro      ; se pudo: hay disco dentro
  451.                MOV   [SI].cambio,ON    ; futura detección tipo disco
  452.                CLC                     ; NO indicar cambio de disco...
  453.                JMP   fin_detecta       ; ...para pasar control a BIOS
  454. disco_dentro:  PUSH  DS
  455.                PUSH  40h
  456.                POP   DS
  457.                MOV   BYTE PTR DS:[41h],6  ; error 'media changed'
  458.                POP   DS
  459.                CMP   AH,5              ; ¿función de formateo?
  460.                JE    fin_detecta_c     ; no perder el tiempo
  461.                MOV   buf_unidad,-1     ; invalidar buffer
  462.                MOV   [SI].gap,20       ; GAP provisional
  463.                MOV   CX,3              ; 3 intentos
  464. intenta_io0:   PUSH  CX
  465.                CMP   CX,2              ; CF=1 la 3ª vez (a 0 si CX<>1)
  466.                CALL  reset_drv
  467.                MOV   [SI].vunidad0,0   ; empezar con 500 Kbit/seg.
  468. intenta_io:    MOV   cilindro,0
  469.                MOV   cabezal,0
  470.                MOV   sector,1          ; sector de arranque
  471.                MOV   seccion,0
  472.                MOV   secciones,1
  473.                MOV   orden,F_READ
  474.                MOV   DI,buffer
  475.                CALL  direct_acceso
  476.                JNE   otra_densidad     ; es otra densidad de disco
  477.                POP   CX
  478.                MOV   BX,buffer
  479.                CALL  set_info          ; características nuevo soporte
  480.                CLC
  481.                JMP   fin_detecta_c     ; indicar cambio de disco
  482. otra_densidad: MOV   AL,[SI].vunidad0
  483.                INC   AX                ; próxima velocidad
  484.                CMP   AL,3
  485.                JA    otro_intento
  486.                MOV   [SI].vunidad0,AL
  487.                JMP   intenta_io        ; probar otra velocidad
  488. otro_intento:  MOV   [SI].vunidad0,0
  489.                POP   CX
  490.                LOOP  intenta_io0       ; reintento
  491. fin_detecta_c: STC                     ; indicar cambio de disco
  492. fin_detecta:   XPOP  <ES, DS>          ; **
  493.                POPA                    ; *
  494.                RET
  495. detecta_cambio ENDP
  496.  
  497. ; ------------ Anotar la información del disquete si es de tipo 2M.
  498. ;              A la entrada, DS:SI apunta a las variables de la unidad
  499. ;              y ES:BX al sector de arranque del disco. Se actualiza
  500. ;              también la variable BIOS de tipo de densidad (la BIOS
  501. ;              no se da cuenta del cambio de disco y conviene ayudar).
  502.  
  503. set_info       PROC
  504.                PUSHA
  505.                CALL  calc_chk
  506.                JC    set_info_exit     ; no es disco 2M
  507.                MOV   [SI].chk,AL         ; anotar checksum
  508.                MOV   [SI].version_fmt,CL ; y versión del formato
  509.                MOV   DL,unidad
  510.                STC
  511.                CALL  set_flag_STV      ; CF = 1 -> indicar disco 2M
  512.                MOV   AL,ES:[BX+22]     ; tamaño de FAT
  513.                MOV   [SI].tam_fat,AL
  514.                MOV   CL,ES:[BX+65]     ; CL a 0 si acceso multi-sector
  515.                MOV   [SI].multi_io,CL
  516.                MOV   AX,ES:[BX+66]
  517.                MOV   [SI].vunidad,AX   ; velocidad pista 0 / demás
  518.                MOV   AL,ES:[BX+24]
  519.                MOV   [SI].sectpista,AL ; sectores/pista
  520.                MOV   DI,ES:[BX+72]
  521.                MOV   AL,ES:[BX+DI+1]   ; GAP de formateo
  522.                MOV   AH,AL
  523.                AND   CL,CL             ; CL a 0 si acceso multi-sector
  524.                JZ    gap_rw_ok         ; GAP R/W para /F
  525.                ADD   AH,190
  526.                MOV   AL,11
  527.                MUL   AH                ; AX = (190+GAP)*11
  528.                SUB   AX,2048+62
  529. gap_rw_ok:     SHR   AL,1              ; GAP R/W para /M
  530.                MOV   [SI].gap,AL
  531.                MOV   CX,maxs
  532.                MOV   DI,ES:[BX+74]
  533.                ADD   DI,BX
  534.                LEA   BX,[SI].tabla_tsect
  535. genera_ts:     MOV   AL,ES:[DI]
  536.                MOV   [BX],AL
  537.                INC   BX
  538.                INC   DI
  539.                LOOP  genera_ts         ; información estructura pistas
  540. set_info_exit: MOV   AL,[SI].vunidad0
  541.                SHL   AL,6              ; velocidad en bits 7:6
  542.                OR    AL,00010111b      ; establecido otro medio físico
  543.                CMP   [SI].tipo_drv,2
  544.                JA    modo_ok           ; es unidad de 3½
  545.                AND   AL,11111000b
  546.                OR    AL,00000101b      ; 1.2 en 1.2
  547.                TEST  AL,01000000b
  548.                JZ    modo_ok
  549.                XOR   AL,00100001b      ; 360 en 1.2 y seek * 2
  550. modo_ok:       PUSH  DS
  551.                MOV   BX,90h
  552.                ADD   BL,unidad
  553.                PUSH  40h
  554.                POP   DS
  555.                AND   BYTE PTR DS:[BX],8  ; respetar bit de 2.88M
  556.                OR    DS:[BX],AL        ; actualizar variable BIOS
  557.                POP   DS                ; con el tipo de densidad
  558.                POPA
  559.                RET
  560. set_info       ENDP
  561.  
  562. ; ------------ Calcular el checksum de la zona vital del sector de
  563. ;              arranque. A la entrada, ES:BX -> sector de arranque.
  564. ;              A la salida, CF=1 si el disco no es 2M; de otro modo
  565. ;              checksum en AL y versión del formato de disco en CL.
  566.  
  567. calc_chk       PROC
  568.                XPUSH <SI, DI>
  569.                LEA   DI,[BX+3]         ; DI=BX+3
  570.                LEA   SI,id_sistema
  571.                MOV   CX,6
  572.                REP   CMPSB             ; comparar identificación
  573.                STC
  574.                JNE   chk_ret           ; el disco no es 2M
  575.                XOR   AX,AX
  576.                MOV   CL,ES:[BX+64]     ; versión del formateador
  577.                CMP   CL,6
  578.                JB    chk_ok            ; no usaba este checksum
  579.                MOV   DI,ES:[BX+68]
  580. chk_sum:       DEC   DI
  581.                ADD   AL,ES:[BX+DI]
  582.                CMP   DI,63
  583.                JA    chk_sum
  584. chk_ok:        CLC
  585. chk_ret:       XPOP  <DI, SI>
  586.                RET
  587. calc_chk       ENDP
  588.  
  589. ; ------------ Determinar el tipo de error producido en el acceso.
  590.  
  591. set_err        PROC
  592.                PUSHA
  593.                JNC   err_ret           ; no hay error
  594.                CMP   status,0          ; ¿'status' ya asignado?
  595.                JNE   err_retc          ; no cambiarlo si es así
  596.                MOV   AL,BYTE PTR fdc_result+1
  597.                AND   AL,10110111b      ; aislar condiciones de test
  598.                LEA   BX,lista_errs
  599.                MOV   CX,9
  600. busca_err:     MOV   AH,[BX]           ; código de error BIOS
  601.                SHL   AL,1
  602.                JC    err_ok            ; es ese error
  603.                INC   BX
  604.                LOOP  busca_err         ; buscar otro error
  605. err_ok:        OR    status,AH
  606. err_retc:      STC                     ; condición de error
  607. err_ret:       POPA
  608.                RET
  609. set_err        ENDP
  610.  
  611. ; ------------ Actualizar variables de error de la BIOS.
  612.  
  613. set_bios_err   PROC
  614.                PUSHF                   ; *
  615.                PUSHA                   ; **
  616.                PUSH  ES                ; ***
  617.                PUSH  40h
  618.                POP   ES
  619.                MOV   DI,41h            ; bytes de resultados del 765
  620.                LEA   SI,status         ; variable BIOS de status y 7
  621.                MOV   CX,4              ; bytes: 4 palabras
  622.                REP   MOVSW
  623.                POP   ES                ; ***
  624.                POPA                    ; **
  625.                POPF                    ; *
  626.                RET
  627. set_bios_err   ENDP
  628.  
  629. ; ------------ Realizar lecturas, escrituras y verificaciones: rutina
  630. ;              que sustituye el código de la BIOS para poder soportar
  631. ;              los formatos 2M. La operación puede quedar dividida en
  632. ;              tres fases: el fragmento anterior a la FAT2, la zona
  633. ;              correspondiente a la FAT2 (se ignora la escritura y se
  634. ;              simula su lectura leyendo la FAT1) y un último bloque
  635. ;              ubicado tras la FAT2. El sector de arranque es emulado
  636. ;              empleando el primer sector físico de la FAT2 (aunque en
  637. ;              los discos de versión de formato anterior a la 7 se usa
  638. ;              el sector de arranque verdadero -permitiendo escribirlo
  639. ;              sólo si es válido-). En cualquier caso, si el número de
  640. ;              cabezal tiene el bit 7 activo, se sobreentiende que el
  641. ;              programa que llama soporta disquetes 2M y no se emula
  642. ;              la FAT2 ni el sector de arranque, para permitirle
  643. ;              acceder al código SuperBOOT. Las coordenadas de la BIOS
  644. ;              se traducen a las unidades del DOS por mayor comodidad.
  645.  
  646. control2m      PROC
  647.                PUSH  DS                ; *
  648.                PUSHA                   ; **
  649.                PUSH  CS
  650.                POP   DS
  651.                MOV   unidad,DL
  652.                CALL  set_SI_drv        ; SI -> variables de la unidad
  653.                CMP   [SI].chk,0
  654.                JE    chk_valido        ; checksum correcto en sector 0
  655.                MOV   status,40h        ; devolver 'Seek Error' al DOS
  656.                JMP   exit_2m_ctrl
  657. chk_valido:    PUSH  AX                ; ***
  658.                MOV   AH,0
  659.                MOV   numsect,AX        ; nº sectores
  660.                MOV   AL,CH             ; cilindro
  661.                SHL   AL,1
  662.                MOV   DL,DH
  663.                AND   DH,01111111b
  664.                ADD   AL,DH             ; cabezal físico
  665.                MUL   [SI].sectpista
  666.                ADD   AL,CL             ; sector
  667.                ADC   AH,0
  668.                DEC   AX                ; AX = nº sector DOS
  669.                MOV   sectini,AX
  670.                MOV   DI,BX             ; ES:DI -> dirección
  671.                POP   BX                ; ***
  672.                MOV   BL,BH
  673.                MOV   BH,0
  674.                MOV   CL,[BX+OFFSET tab_ordenes-2]
  675.                MOV   orden,CL
  676.                SHL   DL,1
  677.                JC    acceso_final      ; cabezal >= 128: no emular
  678.                AND   AX,AX             ; ¿comienza en sector 0?
  679.                JNZ   io_emula          ; no
  680.                CMP   [SI].version_fmt,7
  681.                JB    boot_real         ; no soportado BOOT virtual
  682.                MOV   AL,[SI].tam_fat   ; AH = 0
  683.                INC   AX
  684.                MOV   CX,1              ; sector BOOT emulado en
  685.                CALL  ejecuta_io        ; el primer sector FAT2
  686.                JNE   fin_ctrl
  687. boot_fin_op:   DEC   numsect
  688.                INC   sectini
  689.                MOV   AX,sectini
  690.                JMP   io_emula
  691. boot_real:     CMP   orden,F_WRITE
  692.                JNE   io_emula
  693.                MOV   BX,DI             ; BOOT de 2M 1.3 y anteriores
  694.                CALL  calc_chk
  695.                JC    si_skip           ; no es de tipo 2M
  696.                AND   AL,AL
  697.                JZ    io_emula          ; lo es y con checksum correcto
  698. si_skip:       ADD   DI,512
  699.                JMP   boot_fin_op       ; impedir estropicio de BOOT
  700. io_emula:      MOV   CL,[SI].tam_fat
  701.                MOV   CH,0              ; CX = primer sector FAT2 - 1
  702.                CMP   AX,CX
  703.                JA    en_fat2?          ; ¿la operación afecta a FAT2?
  704.                CALL  calc_iop          ; calcular sectores antes FAT2
  705.                CALL  ejecuta_io        ; CX sectores desde AX
  706.                JNE   fin_ctrl          ; error
  707.                CMP   numsect,0
  708.                JE    fin_ctrl          ; fin de la transferencia
  709. en_fat2?:      MOV   AX,sectini
  710.                MOV   CL,[SI].tam_fat
  711.                MOV   CH,0
  712.                SHL   CX,1              ; CX = último sector FAT2
  713.                CMP   AX,CX
  714.                JA    acceso_final      ; la operación es tras la FAT2
  715.                CALL  calc_iop          ; sectores hasta fin de FAT2
  716.                CMP   orden,F_WRITE
  717.                JNE   emula_fat1
  718.                SHL   CX,9              ; CX = CX * 512
  719.                ADD   DI,CX             ; ES:DI actualizado
  720.                JMP   acceso_final
  721. emula_fat1:    MOV   DL,[SI].tam_fat
  722.                MOV   DH,0
  723.                SUB   AX,DX             ; leer de FAT1 y no de la FAT2
  724.                CALL  ejecuta_io        ; CX sectores desde AX
  725.                JNE   fin_ctrl          ; error
  726. acceso_final:  CMP   numsect,0
  727.                JE    fin_ctrl          ; fin de la transferencia
  728.                MOV   AX,sectini
  729.                MOV   CX,numsect
  730.                CALL  ejecuta_io
  731. fin_ctrl:      CLC
  732.                CALL  motor_off_cnt     ; cuenta normal detención motor
  733.                CALL  set_bios_err      ; actualizar variables BIOS
  734. exit_2m_ctrl:  POPA                    ; **
  735.                MOV   AH,status
  736.                POP   DS                ; *
  737.                AND   AH,AH
  738.                JZ    st_ok             ; resultado correcto (CF=0)
  739.                STC                     ; error
  740.                MOV   AL,0              ; 0 sectores movidos
  741. st_ok:         RET
  742. calc_iop:      SUB   CX,AX
  743.                INC   CX                ; CX sectores
  744.                CMP   CX,numsect
  745.                JBE   nsect_ok
  746.                MOV   CX,numsect        ; sólo quedan CX
  747. nsect_ok:      SUB   numsect,CX
  748.                ADD   sectini,CX
  749.                RET
  750. control2m      ENDP
  751.  
  752. ; ------------ A la entrada, AX indica el sector inicial (coordenadas
  753. ;              del DOS) y CX el número de sectores a procesar.
  754. ;              * Definiciones: «Sector físico» es un sector del disco
  755. ;              de 512, 1024 ó 2048 bytes (números de sector del 1 al N
  756. ;              en la pista). Este sector físico está dividido en
  757. ;              «secciones» de 512 bytes, constando por tanto de 1, 2 ó
  758. ;              4 secciones. «Sector virtual» es el número de sector
  759. ;              del programa que llama a INT 13h, comprendido entre 1 y
  760. ;              M. Esta estructura de N sectores por pista de distintos
  761. ;              tamaños, se verifica en todo el disco con excepción del
  762. ;              cabezal y cilindro 0 (con un formato más convencional
  763. ;              de sectores de 512 bytes numerados de 1 a J, aunque no
  764. ;              existen algunos de los intermedios que corresponden a
  765. ;              la segunda copia de la FAT).
  766. ;              * Primero se convierte el sector virtual (1..M) en su
  767. ;              correspondiente físico (1..J en la pista 0 y 1..N en
  768. ;              las demás), deduciendo qué porción de 512 bytes (o
  769. ;              sección) es afectada. Un sector virtual (512 bytes)
  770. ;              simulado suele ser parte de un sector físico de 2048
  771. ;              bytes en muchos casos. Si dicho sector físico ya había
  772. ;              sido leído al buffer en anteriores accesos, se extrae
  773. ;              la sección necesaria. Si no, se carga del disco y se
  774. ;              extrae dicho fragmento. El número de sectores virtuales
  775. ;              que se solicitan (=secciones) permite realizar un bucle
  776. ;              hasta completar la transferencia; el interleave 1:2 de
  777. ;              los sectores físicos en /M permite acceder sector a
  778. ;              sector sin pérdida de rendimiento. En el caso de la
  779. ;              escritura, se estudia primero si hay varios sectores
  780. ;              virtuales consecutivos que escribir, completando entre
  781. ;              todos un sector físico: en ese caso, se prepara el
  782. ;              mismo y se escribe sin más. En caso de que haya que
  783. ;              modificar sólo una única sección de un sector físico,
  784. ;              salvo si éste es de 512 bytes, no hay más remedio que
  785. ;              cargarlo al buffer (realizar una prelectura),
  786. ;              actualizar la sección correspondiente y volverlo a
  787. ;              escribir.
  788. ;              * En el formato /F se realiza una operación multisector
  789. ;              si es posible y sin emplear el buffer intermedio (si
  790. ;              bien podría ser preciso emplearlo con la primera y
  791. ;              última sección); en los dos formatos de disco se hace
  792. ;              la operación multisector en la primera pista. Las
  793. ;              operaciones multisector puede que sea preciso
  794. ;              dividirlas en tres fases: los sectores antes de una
  795. ;              frontera de DMA, el que la cruza (que es transferido
  796. ;              a través del buffer intermedio) y los que están detrás.
  797.  
  798. ejecuta_io     PROC
  799.                MOV   BX,AX             ; AX = sector DOS inicial
  800.                MOV   secciones,CL      ; CX sectores (CL realmente)
  801.                DIV   [SI].sectpista
  802.                INC   AH                ; numerado desde 1...
  803.                MOV   sector,AH         ; ...el resto es el sector
  804.                SHR   AL,1
  805.                MOV   cilindro,AL       ; cilindro
  806.                RCL   AL,1
  807.                AND   AL,1
  808.                MOV   cabezal,AL        ; cabezal
  809.                MOV   AL,sector
  810.                ADD   AL,secciones
  811.                JC    no_cabe           ; sector+secciones > 255
  812.                DEC   AX                ; DEC AX = DEC AL
  813.                CMP   AL,[SI].sectpista
  814.                JBE   si_cabe
  815. no_cabe:       MOV   status,4          ; 'sector no encontrado'
  816.                JMP   fin_io
  817. si_cabe:       MOV   AL,AH             ; sector en AL
  818.                CBW                     ; sección 0 (AH = 0)
  819.                CALL  pista0?
  820.                JZ    s_xx              ; sector físico en pista/cara 0
  821.                LEA   BX,[SI].tabla_tsect-1
  822.                DEC   AX                ; AH = 0
  823. resta_secc:    INC   BX
  824.                INC   AH
  825.                MOV   CL,[BX]
  826.                SUB   CL,2
  827.                MOV   CH,1
  828.                SHL   CH,CL
  829.                SUB   AL,CH
  830.                JNC   resta_secc        ; en las demás pistas
  831.                ADD   AL,CH
  832.                XCHG  AH,AL
  833. s_xx:          MOV   sector,AL         ; sector lógico convertido a
  834.                MOV   seccion,AH        ; sector y sección físicas
  835. direct_acceso: CALL  motor_ok          ; asegurar que está en marcha
  836.                MOV   AH,0
  837.                MOV   sector_fin,AH     ; no acceder a más de 1 sector
  838.                CALL  pista0?           ; (al menos de momento)
  839.                JNZ   decide_multi      ; no es pista 0
  840.                MOV   AL,secciones
  841.                MOV   secciones,AH      ; las que restan (AH = 0)
  842.                JMP   multi_proc
  843. decide_multi:  CMP   [SI].multi_io,AH  ; AH = 0
  844.                JNE   io_pasos          ; acceso sector a sector
  845.                CMP   seccion,AH
  846.                JE    multi_acc
  847.                CALL  acceso_secc       ; no acceso a inicio sector
  848.                JC    fin_io
  849. multi_acc:     CMP   secciones,AH      ; AH = 0
  850.                JE    fin_io
  851.                CALL  num_secciones
  852.                MOV   CL,AL
  853.                MOV   AL,secciones      ; AH = 0
  854.                DIV   CL
  855.                AND   AL,AL
  856.                JZ    io_pasos          ; no quedan sectores enteros
  857.                MOV   secciones,AH      ; las que restan
  858. multi_proc:    CALL  acceso_multi      ; de AL sectores
  859.                JC    fin_io
  860. io_pasos:      CMP   secciones,0
  861.                JE    fin_io            ; no restan secciones finales
  862.                CALL  acceso_secc
  863.                JNC   io_pasos
  864. fin_io:        CMP   status,0          ; ZF = 1 -> operación correcta
  865.                RET
  866.  
  867. acceso_secc:   PUSH  AX
  868.                CMP   orden,F_WRITE     ; acabar transferencia sector
  869.                JE    escritura
  870.                CMP   orden,F_VERIFY
  871.                JE    verificacion
  872.                CALL  leido?            ; realizar lectura...
  873.                JNC   ya_leido          ; sector ya en el buffer
  874. hay_que_leer:  CALL  acceso_sector     ; efectuar E/S
  875.                JC    acc_ret           ; ha habido fallo
  876. ya_leido:      CALL  trans_secc        ; buffer -> memoria
  877.                JMP   acc_ret
  878. escritura:     CMP   seccion,0
  879.                JNE   prelectura        ; sólo parte del sector cambia
  880.                CALL  num_secciones
  881.                CMP   secciones,AL
  882.                JAE   escribir          ; Todo el sector físico cambia
  883. prelectura:    CALL  leido?            ; Leer el sector físico para
  884.                JNC   escribir          ; cambiar sólo una parte de él
  885.                MOV   orden,F_READ      ; de momento leer...
  886.                CALL  acceso_sector     ; efectuar E/S
  887.                MOV   orden,F_WRITE     ; ... restaurar orden original
  888.                JC    acc_ret           ; ha habido fallo
  889. escribir:      CALL  trans_secc        ; memoria -> buffer
  890.                CALL  acceso_sector     ; volcar buffer al disco
  891.                JMP   acc_ret
  892. verificacion:  PUSH  BX
  893.                MOV   BL,seccion
  894.                CALL  num_secciones
  895. dec_sec_veri:  DEC   secciones
  896.                JZ    verifica
  897.                INC   BX
  898.                CMP   BL,AL
  899.                JB    dec_sec_veri
  900. verifica:      POP   BX
  901.                CALL  acceso_sector     ; leer para forzar verificación
  902. acc_ret:       PUSHF
  903.                INC   sector            ; preparado para otro sector
  904.                MOV   seccion,0         ; desde su primera sección
  905.                POPF
  906.                POP   AX
  907.                RET
  908.  
  909. acceso_multi:  PUSH  AX                ; AL = sectores a transferir
  910.                MOV   BX,ES             ;      desde 'sector' teniendo
  911.                SHL   BX,4              ;      cuidado con el DMA
  912.                ADD   BX,DI
  913.                NEG   BX                ; BX = bytes hasta frontera DMA
  914.                CALL  num_secciones
  915.                MOV   CH,AL             ; AL secciones de 512 bytes
  916.                MOV   CL,0
  917.                SHL   CX,1              ; CX = bytes por sector
  918.                XCHG  AX,BX             ; BL = secciones por sector
  919.                XOR   DX,DX
  920.                DIV   CX
  921.                MOV   CL,AL             ; CL = sectores que caben
  922.                POP   AX                ; AL = sectores a transferir
  923.                CMP   AL,CL
  924.                JA    acc_mult2         ; no hay problemas con el DMA
  925. acc_mult1:     MOV   CL,AL
  926. acc_mult2:     AND   CL,CL
  927.                JZ    acc_mult3         ; primer sector problemático
  928.                MOV   AH,sector
  929.                MOV   sector_ini,AH
  930.                ADD   AH,CL
  931.                DEC   AH
  932.                MOV   sector_fin,AH
  933.                INC   AH
  934.                SUB   AL,CL
  935.                CALL  acceso_sector     ; sectores no problemáticos
  936.                MOV   sector,AH
  937.                JC    acc_mult_fin
  938. acc_mult3:     AND   AL,AL             ; ahora el sector problemático
  939.                JZ    acc_mult_fin
  940.                ADD   secciones,BL      ; compensar futuro decremento
  941.                CALL  acceso_secc       ; a través del buffer auxiliar
  942.                JC    acc_mult_fin
  943.                DEC   AL
  944.                JMP   acc_mult1         ; sectores que restan
  945. acc_mult_fin:  RET
  946. ejecuta_io     ENDP
  947.  
  948. ; ------------ Mover secciones desde el buffer hacia la memoria (con
  949. ;              orden F_READ) después de la lectura o de la memoria al
  950. ;              buffer (orden F_WRITE) antes de la escritura. En la
  951. ;              verificación (orden F_VERIFY) no se mueve nada porque
  952. ;              esta subrutina no es invocada.
  953.  
  954. trans_secc     PROC
  955.                XPUSH <AX, BX, CX, SI>  ; *
  956.                MOV   BL,seccion        ; desde esta sección
  957.                CALL  num_secciones     ; nº secciones del sector
  958. otra_secci:    PUSH  BX
  959.                SHL   BX,9              ; sección * 512
  960.                ADD   BX,buffer         ; dirección
  961.                MOV   SI,BX
  962.                MOV   CX,256            ; tamaño sección (palabras)
  963.                CALL  swap_reg          ; ¿intercambiar origen-destino?
  964.                REP   MOVSW             ; copiar 512 bytes
  965.                CALL  swap_reg          ; ¿intercambiar origen-destino?
  966.                POP   BX
  967.                DEC   secciones         ; una menos
  968.                JZ    fin_secc
  969.                INC   BX                ; otra sección del sector
  970.                CMP   BL,AL             ; ¿sector agotado?
  971.                JB    otra_secci        ; aún no
  972. fin_secc:      XPOP  <SI, CX, BX, AX>  ; *
  973.                RET
  974. swap_reg:      CMP   CS:orden,F_WRITE
  975.                JE    interc
  976.                CLC
  977.                RET
  978. interc:        XCHG  SI,DI             ; en escritura, invertir el
  979.                XPUSH <ES, DS>          ; sentido de la operación
  980.                XPOP  <ES, DS>
  981.                RET
  982. trans_secc     ENDP
  983.  
  984. ; ------------ Comprobar si el sector ya está en el buffer.
  985.  
  986. leido?         PROC
  987.                PUSH  AX
  988.                MOV   AL,buf_unidad
  989.                CMP   AL,unidad
  990.                JNE   no_leido          ; es en otra unidad
  991.                MOV   AL,cilindro
  992.                MOV   AH,cabezal
  993.                CMP   AX,buf_cilcab
  994.                JNE   no_leido          ; es en otro cilindro/cabezal
  995.                MOV   AL,buf_sector
  996.                CMP   AL,sector
  997.                JNE   no_leido          ; es otro sector
  998.                POP   AX
  999.                RET                     ; está en el buffer
  1000. no_leido:      STC
  1001.                POP   AX
  1002.                RET                     ; sector no leído
  1003. leido?         ENDP
  1004.  
  1005. ; ------------ Leer o escribir sector(es). Se selecciona el tamaño de
  1006. ;              sector correcto antes de llamar a sector_io.  En esta
  1007. ;              rutina se actualiza la variable «status» en función de
  1008. ;              los posibles errores de acceso.  Si sector_fin es
  1009. ;              distinto de 0 se accede a los sectores indicados, si es
  1010. ;              0 se accede sólo al sector «sector» a través del buffer
  1011. ;              intermedio y al final se anota el sector cargado ó
  1012. ;              escrito para evitar futuras lecturas innecesarias, a
  1013. ;              modo de mini-caché que dispara la velocidad de acceso a
  1014. ;              sectores lógicos consecutivos.
  1015.  
  1016. acceso_sector  PROC
  1017.                XPUSH <AX, BX>
  1018.                CALL  seek_drv          ; posicionar el cabezal
  1019.                JNC   en_pista
  1020.                CMP   status,0          ; ¿error ya determinado?
  1021.                JNE   acc_fin_err
  1022.                OR    status,40h        ; no: pues 'seek error'
  1023. acc_fin_err:   STC
  1024.                JMP   acceso_fin
  1025. en_pista:      CALL  pista0?
  1026.                MOV   AL,2
  1027.                JZ    tam_acc_ok        ; sectores 512 en cil./cab. 0
  1028.                LEA   BX,[SI].tabla_tsect
  1029.                ADD   BL,sector
  1030.                ADC   BH,0
  1031.                MOV   AL,[BX-1]
  1032. tam_acc_ok:    MOV   tsector,AL
  1033.                CMP   sector_fin,0      ; ¿usar buffer intermedio?
  1034.                JE    acceso_buffer
  1035.                CALL  sector_io
  1036.                MOV   sector_fin,0      ; no acceder a más de 1 sector
  1037.                PUSHF                   ; **1
  1038.                JMP   acceso_rep        ; en el futuro (por defecto)
  1039. acceso_buffer: XPUSH <ES, DI>
  1040.                PUSH  CS
  1041.                POP   ES
  1042.                MOV   DI,buffer         ; acceso con buffer auxiliar
  1043.                MOV   AL,sector         ; mismo sector inicial/final
  1044.                MOV   sector_ini,AL
  1045.                MOV   sector_fin,AL
  1046.                CALL  sector_io
  1047.                MOV   sector_fin,0
  1048.                XPOP  <DI, ES>
  1049.                PUSHF                   ; **2
  1050.                MOV   AL,-1             ; invalidar contenido buffer
  1051.                JC    acceso_anota      ; si hay error
  1052.                CMP   orden,F_VERIFY
  1053.                JE    acceso_rep        ; nada leído físicamente
  1054.                MOV   AL,unidad
  1055. acceso_anota:  MOV   buf_unidad,AL
  1056.                MOV   AL,cilindro
  1057.                MOV   AH,cabezal
  1058.                MOV   buf_cilcab,AX
  1059.                MOV   AL,sector
  1060.                MOV   buf_sector,AL     ; anotado el sector en buffer
  1061. acceso_rep:    POPF                    ; ** mucho cuidado con la pila
  1062.                CALL  set_err           ; ajustar variable «status»
  1063. acceso_fin:    XPOP  <BX, AX>
  1064.                RET
  1065. acceso_sector  ENDP
  1066.  
  1067. ; ------------ Devolver el número de secciones del sector en curso.
  1068.  
  1069. num_secciones  PROC
  1070.                CALL  pista0?
  1071.                MOV   AL,1
  1072.                JZ    num_secc_ok       ; sectores 512 en cil./cab. 0
  1073.                XPUSH <BX, CX>
  1074.                LEA   BX,[SI].tabla_tsect
  1075.                ADD   BL,sector
  1076.                ADC   BH,0
  1077.                MOV   CL,[BX-1]
  1078.                SUB   CL,2
  1079.                MOV   AL,1
  1080.                SHL   AL,CL
  1081.                XPOP  <CX, BX>
  1082. num_secc_ok:   RET                     ; resultado en AL
  1083. num_secciones  ENDP
  1084.  
  1085. ; ------------ Asegurar que el motor está en marcha.
  1086.  
  1087. motor_ok       PROC
  1088.                PUSHA                   ; *
  1089.                PUSH  DS                ; **
  1090.                MOV   BX,40h
  1091.                PUSH  BX
  1092.                POP   DS
  1093.                MOV   CH,255-18         ; CH = 255 - 1 segundo
  1094.                CLI
  1095.                MOV   CL,CS:unidad
  1096.                MOV   AL,1
  1097.                SHL   AL,CL
  1098.                TEST  [BX-1],AL         ; ¿motor en marcha?
  1099.                JZ    arrancarlo        ; arrancarlo
  1100.                CMP   [BX],CH           ; Si encendido y acelerado...
  1101.                JBE   ok_motor          ; ...seguir
  1102. arrancarlo:    OR    [BX-1],AL         ; arrancar motor
  1103.                AND   BYTE PTR [BX-1],0CFh  ; borrar número unidad
  1104.                MOV   AL,CL
  1105.                SHL   AL,4              ; unidad << 4
  1106.                OR    [BX-1],AL         ; nuevo número de unidad
  1107.                MOV   BYTE PTR [BX],255 ; asegurar que no se pare
  1108.                STI
  1109.                MOV   DX,3F2h           ; registro de salida digital
  1110.                ADD   CL,4
  1111.                MOV   AL,1
  1112.                SHL   AL,CL             ; colocar bit del motor
  1113.                OR    AL,CS:unidad      ; seleccionar unidad
  1114.                OR    AL,00001100b      ; modo DMA, no hacer reset
  1115.                OUT   DX,AL             ; poner en marcha el motor
  1116.                MOV   AX,90FDh
  1117.                CLC
  1118.                INT   15h               ; permitir multitarea
  1119.                JC    ok_motor          ; timeout
  1120.                MOV   AX,1000           ; 1 segundo aceleración
  1121.                CALL  retardo           ; esperar aceleración disco
  1122. ok_motor:      MOV   [BX],CH           ; cuenta máxima detención motor
  1123.                STI                     ; sin forzar futura aceleración
  1124.                POP   DS                ; **
  1125.                POPA                    ; *
  1126.                RET
  1127. motor_ok       ENDP
  1128.  
  1129. ; ------------ Establecer modalidad de operación del controlador
  1130. ;              y poner el motor en marcha. Si CF=1 se le da tiempo
  1131. ;              además a la unidad para que acelere.
  1132.  
  1133. reset_drv      PROC
  1134.                PUSHA
  1135.                CALL  motor_off_cnt     ; cuenta detención motor
  1136.                MOV   CL,unidad
  1137.                MOV   AL,CL
  1138.                SHL   AL,2              ; unidad seleccionada
  1139.                OR    AL,1              ; bit de motor
  1140.                SHL   AL,CL             ; colocar dicho bit
  1141.                PUSH  DS                ; *
  1142.                PUSH  40h
  1143.                POP   DS
  1144.                CLI
  1145.                MOV   DS:[3Fh],AL
  1146.                AND   BYTE PTR DS:[3Eh],70h ; bit IRQ=0 y recalibrar
  1147.                POP   DS                ; *
  1148.                SHL   AL,4              ; bits motor en nibble alto
  1149.                OR    AL,CL             ; seleccionar unidad
  1150.                OR    AL,00001000b      ; interrupciones+DMA y reset
  1151.                MOV   DX,3F2h           ; registro de salida digital
  1152.                OUT   DX,AL             ; señal de reset
  1153.                CALL  fdc_respiro       ; tiempo reconocer reset en 486
  1154.                OR    AL,00000100b
  1155.                OUT   DX,AL             ; fin de señal de reset
  1156.                CALL  espera_int        ; rehabilitará interrupciones
  1157.                MOV   AL,8
  1158.                CALL  fdc_write         ; comando 'leer estado int...'
  1159.                CALL  fdc_read
  1160.                CALL  fdc_read
  1161.                CALL  envia_specify     ; comando 'specify' adecuado
  1162.                POPA
  1163.                RET
  1164. reset_drv      ENDP
  1165.  
  1166. ; ------------ Enviar comando specify a la controladora. El step-rate
  1167. ;              se selecciona según la densidad, para evitar un sonido
  1168. ;              extraño al posicionar o recalibrar el cabezal.
  1169.  
  1170. envia_specify  PROC
  1171.                PUSH  AX
  1172.                PUSH  DS
  1173.                PUSH  40h
  1174.                POP   DS
  1175.                MOV   AH,DS:[8Bh]
  1176.                POP   DS
  1177.                MOV   AL,3              ; comando 'specify'
  1178.                CALL  fdc_write
  1179.                MOV   AL,0BFh           ; step rate para 500 kbps
  1180.                AND   AH,11000000b
  1181.                JZ    spec1_ok
  1182.                MOV   AL,0AFh           ; step rate para 1 Mbps
  1183.                CMP   AH,11000000b
  1184.                JE    spec1_ok
  1185.                MOV   AL,0DFh           ; step rate para 250/300 Kbps
  1186. spec1_ok:      CALL  fdc_write
  1187.                MOV   AL,2
  1188.                CALL  fdc_write         ; head load y modo DMA
  1189.                POP   AX
  1190.                RET
  1191. envia_specify  ENDP
  1192.  
  1193. ; ------------ Recargar cuenta para la detención del motor. Si CF=1 al
  1194. ;              entrar, se establece la mayor cuenta posible; en caso
  1195. ;              contrario, se pone el valor normal de la tabla base.
  1196.  
  1197. motor_off_cnt  PROC
  1198.                PUSHA
  1199.                PUSH  DS
  1200.                MOV   AL,0FFh           ; valor máximo
  1201.                JC    motor_off_ok
  1202.                PUSH  0
  1203.                POP   DS
  1204.                LDS   BX,DWORD PTR DS:[1Eh*4] ; DS:BX -> INT 1Eh
  1205.                MOV   AL,[BX+2]               ; byte 2 tabla base disco
  1206. motor_off_ok:  PUSH  40h
  1207.                POP   DS
  1208.                MOV   BYTE PTR DS:[40h],AL  ; cuenta parada motor
  1209.                POP   DS
  1210.                POPA
  1211.                RET
  1212. motor_off_cnt  ENDP
  1213.  
  1214. ; ------------ Llevar el cabezal a la pista indicada, recalibrando si
  1215. ;              hubo un reset (se invocó la función 0 de la INT 13h o
  1216. ;              se ejecutó reset_drv) antes de esta operación. Primero
  1217. ;              se selecciona la velocidad de transferencia y se borra
  1218. ;              el resultado de cualquier operación anterior, para que
  1219. ;              todo quede listo para el próximo acceso a disco.
  1220.  
  1221. seek_drv       PROC
  1222.                PUSHA
  1223.                CALL  set_rate          ; velocidad / borrar resultados
  1224.                CALL  envia_specify     ; comando 'specify' adecuado
  1225.                MOV   AH,1
  1226.                MOV   CL,unidad
  1227.                SHL   AH,CL             ; AH = 1 (A:) ó 2 (B:)
  1228.                PUSH  DS
  1229.                PUSH  40h
  1230.                POP   DS
  1231.                TEST  AH,DS:[3Eh]
  1232.                POP   DS
  1233.                JNZ   do_seek           ; la unidad ya fue recalibrada
  1234.                CALL  recalibrar
  1235.                JC    fallo_seek        ; fallo al recalibrar
  1236. do_seek:       MOV   BX,94h
  1237.                ADD   BL,unidad
  1238.                MOV   AL,cilindro
  1239.                PUSH  DS                ; *
  1240.                PUSH  40h
  1241.                POP   DS
  1242.                OR    DS:[3Eh],AH       ; unidad ya recalibrada
  1243.                MOV   AH,DS:[41h]       ; código de error previo
  1244.                CMP   AL,[BX]
  1245.                MOV   [BX],AL
  1246.                POP   DS                ; *
  1247.                JNE   hacer_seek        ; seek necesario
  1248.                CMP   AH,40h            ; ¿error de seek previo?
  1249.                JNE   seek_ok           ; no, evitar seek innecesario
  1250. hacer_seek:    MOV   AL,0Fh
  1251.                CALL  fdc_write         ; comando 'seek'
  1252.                JC    fallo_seek
  1253.                MOV   AL,cabezal
  1254.                SHL   AL,2
  1255.                OR    AL,unidad
  1256.                CALL  fdc_write         ; enviar HD, US1, US0
  1257.                MOV   AL,cilindro
  1258.                CALL  fdc_write         ; enviar cilindro
  1259.                CALL  espera_int        ; esperar interrupción
  1260.                JC    fallo_seek
  1261.                MOV   AL,8
  1262.                CALL  fdc_write         ; comando 'leer estado int...'
  1263.                JC    fallo_seek
  1264.                CALL  fdc_read          ; leer registro de estado 0
  1265.                JC    fallo_seek
  1266.                MOV   AH,AL
  1267.                CALL  fdc_read          ; leer cilindro actual
  1268.                TEST  AH,11000000b      ; comprobar ST0
  1269.                JNZ   fallo_seek
  1270.                MOV   AL,15             ; estabilización para escritura
  1271.                CMP   orden,F_WRITE
  1272.                JE    rseek_ok
  1273.                MOV   AL,1              ; estabilización para lectura
  1274. rseek_ok:      CBW                     ; AH = 0
  1275.                CALL  retardo           ; esperar asentamiento cabezal
  1276. seek_ok:       POPA
  1277.                CLC                     ; retornar con éxito
  1278.                RET
  1279. fallo_seek:    POPA
  1280.                STC                     ; retornar indicando fallo
  1281.                RET
  1282. seek_drv       ENDP
  1283.  
  1284. ; ------------ Establecer velocidad de transferencia correcta si aún
  1285. ;              no ha sido seleccionada y borrar el resultado de otra
  1286. ;              operación previa.
  1287.  
  1288. set_rate       PROC
  1289.                PUSHA
  1290.                CALL  pista0?
  1291.                MOV   AX,[SI].vunidad   ; velocidad pista 0 / demás
  1292.                JZ    vel_ok
  1293.                MOV   AL,AH
  1294. vel_ok:        PUSH  DS                ; *
  1295.                PUSH  40h
  1296.                POP   DS
  1297.                MOV   AH,DS:[8Bh]
  1298.                SHR   AH,6              ; aislar bits de velocidad
  1299.                CMP   AL,AH
  1300.                JE    vel_set           ; velocidad ya seleccionada
  1301.                MOV   DX,3F7h
  1302.                OUT   DX,AL             ; seleccionarla
  1303.                SHL   AL,6
  1304.                AND   BYTE PTR DS:[8Bh],00111111b
  1305.                OR    DS:[8Bh],AL
  1306. vel_set:       POP   DS                ; *
  1307.                LEA   DI,status
  1308.                MOV   CX,8
  1309. borra_status:  MOV   [DI],CH           ; borrar información de estado
  1310.                INC   DI
  1311.                LOOP  borra_status
  1312.                POPA
  1313.                RET
  1314. set_rate       ENDP
  1315.  
  1316. ; ------------ Recalibrar la unidad (si hay error se intenta otra vez
  1317. ;              para el caso de que deba moverse más de 77 pistas).
  1318.  
  1319. recalibrar     PROC
  1320.                PUSHA
  1321.                MOV   BX,94h
  1322.                ADD   BL,unidad
  1323.                PUSH  DS                ; *
  1324.                PUSH  40h
  1325.                POP   DS
  1326.                MOV   [BX],BH           ; pista actual = 0
  1327.                POP   DS                ; *
  1328.                MOV   CX,2              ; dos veces como mucho
  1329. recalibra:     MOV   AL,7
  1330.                CALL  fdc_write         ; comando de 'recalibrado'
  1331.                JC    fallo_recal
  1332.                MOV   AL,cabezal
  1333.                SHL   AL,2
  1334.                OR    AL,unidad
  1335.                CALL  fdc_write         ; enviar HD, US1, US0
  1336.                JC    fallo_recal
  1337.                CALL  espera_int        ; esperar interrupción
  1338.                JC    fallo_recal
  1339.                MOV   AL,8
  1340.                CALL  fdc_write         ; comando 'leer estado int...'
  1341.                JC    fallo_recal
  1342.                CALL  fdc_read          ; leer registro de estado 0
  1343.                JC    fallo_recal
  1344.                MOV   AH,AL
  1345.                CALL  fdc_read          ; leer cilindro actual
  1346.                XOR   AH,00100000b      ; bajar bit de 'seek end'
  1347.                TEST  AH,11110000b      ; comprobar resultado y ST0
  1348.                JNZ   fallo_recal       ; sin 'seek end' o TRK0
  1349.                MOV   AX,1              ; pausa de 1 ms
  1350.                CALL  retardo
  1351.                JMP   recal_ret
  1352. fallo_recal:   LOOP  recalibra         ; reintentar comando
  1353.                STC                     ; condición de fallo
  1354. recal_ret:     POPA
  1355.                RET
  1356. recalibrar     ENDP
  1357.  
  1358. ; ------------ Cargar o escribir sector(es) del disco en ES:DI,
  1359. ;              actualizando la dirección en ES:DI pero sin alterar
  1360. ;              ningún otro registro. Si hay error se devuelve CF=1 y
  1361. ;              no se modifica ES:DI. A partir de fdc_result se dejan
  1362. ;              los 7 bytes que devuelve el FDC al final del acceso.
  1363. ;              En caso de verificación (F_VERIFY) se programa el DMA
  1364. ;              para que no realice transferencia física (convenio de
  1365. ;              las BIOS con fecha 15/11/85 y posterior).
  1366.  
  1367. sector_io      PROC
  1368.                XPUSH <AX, BX, CX, DX>
  1369.                MOV   CL,tsector
  1370.                MOV   CH,0
  1371.                STC
  1372.                RCL   CH,CL
  1373.                MOV   CL,0              ; nº de bytes por sector
  1374.                MOV   AL,sector_fin
  1375.                SUB   AL,sector_ini
  1376.                INC   AX
  1377.                CBW                     ; AX sectores (AH = 0)
  1378.                MUL   CX
  1379.                MOV   DX,AX             ; bytes totales
  1380.                MOV   CX,AX
  1381.                DEC   CX                ; bytes totales - 1
  1382.                MOV   AX,ES
  1383.                CALL  calc_dir_DMA      ; AX:DI -> base BX y página AH
  1384.                MOV   AL,orden          ; modo DMA necesario
  1385.                CALL  prepara_DMA
  1386.                CMP   AL,F_WRITE
  1387.                MOV   AL,11000101b      ; comando de escritura del FDC
  1388.                JE    orden_io_ok
  1389.                MOV   AL,11100110b      ; comando leer (verif.) del FDC
  1390. orden_io_ok:   CALL  fdc_write         ; comando leer/escribir del FDC
  1391.                JC    sector_io_ko
  1392.                MOV   AL,cabezal
  1393.                SHL   AL,2
  1394.                OR    AL,unidad
  1395.                CALL  fdc_write         ; byte 1 de la orden
  1396.                MOV   AL,cilindro
  1397.                CALL  fdc_write         ; enviar cilindro
  1398.                MOV   AL,cabezal
  1399.                CALL  fdc_write         ; enviar cabezal
  1400.                MOV   AL,sector_ini
  1401.                CALL  fdc_write         ; enviar nº sector
  1402.                MOV   AL,tsector
  1403.                CALL  fdc_write         ; longitud sector
  1404.                MOV   AL,sector_fin
  1405.                CALL  fdc_write         ; último sector
  1406.                MOV   AL,[SI].gap
  1407.                CALL  fdc_write         ; GAP de lectura/escritura
  1408.                MOV   AL,128
  1409.                CALL  fdc_write         ; tamaño sector si longitud=0
  1410.                CALL  espera_int
  1411.                PUSHF                   ; *
  1412.                LEA   BX,fdc_result
  1413.                MOV   CX,7
  1414. sect_io_res:   CALL  fdc_read          ; leyendo resultados
  1415.                MOV   [BX],AL
  1416.                INC   BX
  1417.                LOOP  sect_io_res
  1418.                POPF                    ; *
  1419.                JC    sector_io_ko
  1420.                TEST  fdc_result,11000000b
  1421.                JNZ   sector_io_ko
  1422.                ADD   DI,DX             ; actualizar dirección
  1423.                CLC                     ; Ok
  1424.                JMP   sector_io_fin
  1425. sector_io_ko:  STC                     ; indicar fallo
  1426. sector_io_fin: XPOP  <DX, CX, BX, AX>
  1427.                RET
  1428. sector_io      ENDP
  1429.  
  1430. ; ------------ Devolver en AH la página de DMA y en BX la base. A la
  1431. ;              entrada, AX:DI -> dirección de memoria y CX = bytes-1.
  1432. ;              Se supone que el buffer no cruza una frontera de DMA.
  1433.  
  1434. calc_dir_DMA   PROC
  1435.                PUSH  DX
  1436.                MOV   BX,16
  1437.                MUL   BX
  1438.                ADD   AX,DI
  1439.                ADC   DX,0              ; DX:AX = dirección 20 bits
  1440.                MOV   BX,AX             ; base en BX
  1441.                MOV   AH,DL             ; página
  1442.                POP   DX
  1443.                RET
  1444. calc_dir_DMA   ENDP
  1445.  
  1446. ; ------------ Crear tabla con información para formatear. En ES:BX
  1447. ;              está el futuro sector de arranque del disquete.
  1448.  
  1449. genera_info    PROC
  1450.                PUSHA
  1451.                MOV   buf_unidad,-1     ; invalidar contenido buffer
  1452.                MOV   SI,buffer
  1453.                MOV   DI,BX
  1454.                CALL  pista0?
  1455.                JNZ   no_cilcab0             ; no es cilindro/cabezal 0
  1456.                ADD   DI,ES:[BX+70]          ; DI -> datos pista 0
  1457.                MOV   CL,ES:[DI]
  1458.                MOV   CH,0                   ; CX sectores en pista 0
  1459.                INC   DI
  1460.                MOV   AL,ES:[DI]             ; GAP para pista 0
  1461.                MOV   AH,0                   ; byte de relleno
  1462.                INC   DI
  1463.                MOV   BYTE PTR [SI],2        ; tamaño de sector
  1464.                MOV   BYTE PTR [SI+1],CL     ; número de sectores
  1465.                MOV   [SI+2],AX              ; GAP / byte de relleno
  1466. genera_0:      ADD   SI,4
  1467.                MOV   AL,cilindro
  1468.                MOV   AH,cabezal
  1469.                MOV   [SI],AX                ; datos para cada sector
  1470.                MOV   AL,ES:[DI]
  1471.                MOV   AH,2                   ; LOG2 (tamaño)-7
  1472.                INC   DI
  1473.                MOV   [SI+2],AX              ; nº de sector / tamaño
  1474.                LOOP  genera_0
  1475.                POPA
  1476.                RET
  1477. no_cilcab0:    ADD   DI,ES:[BX+72]
  1478.                CMP   BYTE PTR ES:[BX+65],1
  1479.                JE    info_stv
  1480.                MOV   DL,ES:[DI+2]           ; tamaño /F
  1481.                MOV   DH,ES:[DI]             ; nº sectores
  1482.                MOV   [SI],DX
  1483.                XCHG  DH,DL                  ; tamaño en DH
  1484.                MOV   CL,DL
  1485.                MOV   CH,0                   ; CX sectores
  1486.                MOV   AL,ES:[DI+1]           ; GAP para formatear
  1487.                MOV   AH,0                   ; byte relleno /F
  1488.                MOV   [SI+2],AX              ; GAP / byte de relleno
  1489.                PUSH  DX
  1490.                MOV   AX,ES:[DI+3]
  1491.                PUSH  AX
  1492.                ADD   AL,AH
  1493.                MUL   cilindro
  1494.                MOV   DX,AX
  1495.                POP   AX
  1496.                MUL   cabezal
  1497.                ADD   AX,DX
  1498.                XOR   DX,DX
  1499.                MOV   BL,ES:[DI]
  1500.                MOV   BH,0
  1501.                DIV   BX                ; DL = módulo
  1502.                SUB   DL,ES:[DI]
  1503.                NEG   DL
  1504.                MOV   AL,DL
  1505.                POP   DX                ; restaurar tamaño en DH
  1506.                MOV   DL,AL             ; primer sector de la pista - 1
  1507.                MOV   BL,ES:[DI]        ; nº sectores en la pista
  1508. genera_pn:     ADD   SI,4
  1509.                INC   DX
  1510.                CMP   DL,BL
  1511.                JBE   ns_ok
  1512.                MOV   DL,1              ; empezar desde el 1
  1513. ns_ok:         MOV   AL,cilindro
  1514.                MOV   AH,cabezal
  1515.                MOV   [SI],AX           ; datos para cada sector
  1516.                MOV   [SI+2],DX         ; nº sector / LOG2 (tamaño)-7
  1517.                LOOP  genera_pn
  1518.                POPA
  1519.                RET
  1520. info_stv:      MOV   CH,ES:[DI]             ; nº sectores
  1521.                MOV   CL,0                   ; CL:CH sectores
  1522.                MOV   [SI],CX                ; tamaño (CL=0) y número
  1523.                XCHG  CH,CL                  ; CX sectores
  1524.                MOV   AL,ES:[DI+1]           ; GAP para formatear
  1525.                MOV   AH,4Eh                 ; byte de relleno /M
  1526.                MOV   [SI+2],AX              ; GAP / byte de relleno
  1527.                MOV   DL,128
  1528. genera_otro:   ADD   SI,4
  1529.                INC   DX
  1530.                MOV   AL,cilindro
  1531.                MOV   AH,cabezal
  1532.                MOV   [SI],AX           ; datos para cada sector
  1533.                XPUSH <CX, DI>          ; *
  1534.                MOV   CL,ES:[DI+2]      ; CH está a 0
  1535. busca_num:     ADD   DI,3
  1536.                CMP   DL,ES:[DI]
  1537.                MOV   AX,ES:[DI+1]      ; número de sector / tamaño
  1538.                JE    hallado           ; es sector a cambiar número
  1539.                LOOP  busca_num
  1540.                MOV   AL,DL             ; no cambiar número
  1541.                MOV   AH,0              ; e indicar tamaño 128
  1542. hallado:       XPOP  <DI, CX>          ; *
  1543.                MOV   [SI+2],AX         ; nº sector / LOG2 (tamaño)-7
  1544.                LOOP  genera_otro
  1545.                POPA
  1546.                RET
  1547. genera_info    ENDP
  1548.  
  1549. ; ------------ Formatear una pista.
  1550.  
  1551. formatea_pista PROC
  1552.                PUSHA
  1553.                MOV   BX,buffer
  1554.                MOV   DI,BX
  1555.                MOV   CL,[BX+1]
  1556.                MOV   CH,0              ; CX sectores
  1557.                SHL   CX,2
  1558.                DEC   CX                ; nº de bytes - 1
  1559.                MOV   AX,DS
  1560.                CALL  calc_dir_DMA      ; AX:DI -> base BX y página AH
  1561.                MOV   AL,4Ah            ; modo DMA para escribir
  1562.                ADD   BX,4              ; saltar primeros 4 bytes
  1563.                CALL  prepara_DMA
  1564.                MOV   BX,buffer
  1565.                MOV   AL,F_FORMAT
  1566.                CALL  fdc_write
  1567.                JC    fallo_fmt
  1568.                MOV   AL,cabezal
  1569.                SHL   AL,2
  1570.                OR    AL,unidad
  1571.                CALL  fdc_write         ; byte 1 de la orden
  1572.                JC    fallo_fmt
  1573.                MOV   CX,4
  1574. format_cmd:    MOV   AL,[BX]
  1575.                CALL  fdc_write
  1576.                INC   BX
  1577.                LOOP  format_cmd
  1578.                CALL  espera_int
  1579. fallo_fmt:     PUSHF
  1580.                LEA   BX,fdc_result
  1581.                MOV   CX,7
  1582. format_res:    CALL  fdc_read          ; leyendo resultados
  1583.                MOV   [BX],AL
  1584.                INC   BX
  1585.                LOOP  format_res
  1586.                POPF
  1587.                JC    fallo_format
  1588.                TEST  fdc_result,11000000b
  1589.                JZ    format_ret
  1590. fallo_format:  STC                     ; fallo
  1591. format_ret:    POPA
  1592.                RET
  1593. formatea_pista ENDP
  1594.  
  1595. ; ------------ Esperar interrupción de disquete durante casi 2
  1596. ;              segundos antes de considerar que ha sido un fracaso.
  1597.  
  1598. espera_int     PROC
  1599.                STI
  1600.                PUSHA
  1601.                XPUSH <DS, 40h>
  1602.                POP   DS
  1603.                MOV   AX,9001h
  1604.                CLC
  1605.                INT   15h               ; permitir multitarea
  1606.                MOV   DX,0280h
  1607.                MOV   BX,3Eh
  1608.                JC    timeout_int
  1609. esp_int_1s:    XOR   CX,CX
  1610. esp_int:       TEST  [BX],DL           ; ¿llegó la interrupción?
  1611.                JNZ   fin_espera
  1612.                PMICRO
  1613.                LOOP  esp_int           ; esperar durante casi 1 seg.
  1614.                DEC   DH
  1615.                JNZ   esp_int_1s
  1616. timeout_int:   OR    CS:status,DL      ; timeout
  1617.                STC
  1618. fin_espera:    PUSHF
  1619.                AND   BYTE PTR [BX],7Fh ; para la próxima vez
  1620.                POPF
  1621.                POP   DS
  1622.                POPA
  1623.                RET
  1624. espera_int     ENDP
  1625.  
  1626. ; ------------ Preparar DMA para E/S. A la entrada, BX = dirección de
  1627. ;              base, AH = registro de página y CX = nº bytes - 1.
  1628.  
  1629. prepara_DMA    PROC
  1630.                PUSH  AX
  1631.                CLI
  1632.                OUT   0Bh,AL            ; registro de modo del DMA
  1633.                MOV   AL,0
  1634.                DELAY
  1635.                OUT   0Ch,AL            ; clear first/last flip-flop
  1636.                MOV   AL,BL
  1637.                DELAY
  1638.                OUT   4,AL
  1639.                MOV   AL,BH
  1640.                DELAY
  1641.                OUT   4,AL              ; enviada dirección base
  1642.                DELAY
  1643.                MOV   AL,AH
  1644.                OUT   81h,AL            ; registro de página del DMA
  1645.                MOV   AL,CL
  1646.                DELAY
  1647.                OUT   5,AL
  1648.                MOV   AL,CH
  1649.                DELAY
  1650.                OUT   5,AL              ; enviada cuenta de bytes
  1651.                STI
  1652.                MOV   AL,2
  1653.                DELAY
  1654.                OUT   0Ah,AL            ; habilitar canal 2 de DMA
  1655.                POP   AX
  1656.                RET
  1657. prepara_DMA    ENDP
  1658.  
  1659. ; ------------ Recibir byte del FDC en AL. A la vuelta, CF=1 si
  1660. ;              la operación fracasó (el FDC no estaba listo) y
  1661. ;              se indica la condición de timeout en «status».
  1662.  
  1663. fdc_read       PROC
  1664.                XPUSH <CX, DX, AX>
  1665.                CALL  fdc_respiro       ; no abrasar el FDC
  1666.                MOV   DX,3F4h           ; registro de estado del FDC
  1667.                MOV   CX,133            ; constante para 0,002 segundos
  1668. espera_rd:     DELAY
  1669.                IN    AL,DX
  1670.                AND   AL,11000000b
  1671.                CMP   AL,11000000b      ; ¿dato listo?
  1672.                JE    fdc_rd_ok
  1673.                DELAY
  1674.                IN    AL,61h
  1675.                AND   AL,10h
  1676.                CMP   AL,AH
  1677.                JE    espera_rd         ; reintentarlo durante 15,09 µs
  1678.                MOV   AH,AL
  1679.                LOOP  espera_rd
  1680.                XPOP  <AX, DX, CX>
  1681.                OR    status,80h        ; timeout
  1682.                MOV   AL,0
  1683.                STC                     ; fallo
  1684.                RET
  1685. fdc_rd_ok:     POP   AX
  1686.                INC   DX                ; apuntar al registro de datos
  1687.                DELAY
  1688.                IN    AL,DX             ; leer byte del FDC
  1689.                XPOP  <DX, CX>
  1690.                CLC                     ; Ok
  1691.                RET
  1692. fdc_read       ENDP
  1693.  
  1694. ; ------------ Enviar byte AL al FDC. A la vuelta, CF=1 si
  1695. ;              la operación fracasó (el FDC no estaba listo) y
  1696. ;              se indica la condición de timeout en «status».
  1697.  
  1698. fdc_write      PROC
  1699.                XPUSH <CX, DX, AX>
  1700.                CALL  fdc_respiro       ; no abrasar el FDC
  1701.                MOV   DX,3F4h           ; registro de estado del FDC
  1702.                MOV   CX,133            ; constante para 0,002 segundos
  1703. espera_wr:     DELAY
  1704.                IN    AL,DX
  1705.                TEST  AL,80h            ; ¿listo para E/S?
  1706.                JNZ   fdc_wr_ok
  1707.                DELAY
  1708.                IN    AL,61h
  1709.                AND   AL,10h
  1710.                CMP   AL,AH
  1711.                JE    espera_wr         ; reintentarlo durante 15,09 µs
  1712.                MOV   AH,AL
  1713.                LOOP  espera_wr
  1714.                XPOP  <AX, DX, CX>
  1715.                OR    status,80h        ; timeout
  1716.                STC                     ; fallo
  1717.                RET
  1718. fdc_wr_ok:     INC   DX                ; apuntar al registro de datos
  1719.                POP   AX
  1720.                DELAY
  1721.                OUT   DX,AL             ; enviar byte al FDC
  1722.                XPOP  <DX, CX>
  1723.                CLC                     ; Ok
  1724.                RET
  1725. fdc_write      ENDP
  1726.  
  1727. ; ------------ Retardo de 60 µs para dar tiempo al FDC en 486 rápidos.
  1728.  
  1729. fdc_respiro    PROC
  1730.                XPUSH <AX, CX>
  1731.                MOV   CX,4
  1732. fdc_ret:       PMICRO
  1733.                LOOP  fdc_ret
  1734.                XPOP  <CX, AX>
  1735.                RET
  1736. fdc_respiro    ENDP
  1737.  
  1738. ; ------------ Esperar exactamente AX milisegundos.
  1739.  
  1740. retardo        PROC
  1741.                PUSHF
  1742.                PUSHA
  1743.                MOV   DX,16970          ; 16970 = 1193180/18*256/1000
  1744.                MUL   DX
  1745.                MOV   CL,AH             ; dividir DX:AX entre 256 y
  1746.                MOV   CH,DL             ; dejar el resultado en DX:CX
  1747.                MOV   DL,DH
  1748.                MOV   DH,0              ; DX:CX 15,09 µs-avos
  1749. retardando:    PMICRO
  1750.                LOOP  retardando
  1751.                AND   DX,DX
  1752.                JZ    retardado
  1753.                DEC   DX
  1754.                JMP   retardando
  1755. retardado:     POPA
  1756.                POPF
  1757.                RET
  1758. retardo        ENDP
  1759.  
  1760.                ; --- Ubicación del sector de hasta 2048 bytes.
  1761.  
  1762.                EVEN
  1763. buffer_io      EQU   $
  1764. tbuffer        EQU   2048
  1765.  
  1766. fin_residente  EQU   $  ; fin del área residente sin contar el buffer
  1767.  
  1768. bytes_resid    EQU   fin_residente-ini_residente
  1769.  
  1770.  
  1771. ; *****************************
  1772. ; *                           *
  1773. ; *   I N S T A L A C I O N   *
  1774. ; *                           *
  1775. ; *****************************
  1776.  
  1777. main           PROC
  1778.                ADD   SP,2              ; quitar dirección de retorno
  1779.                XPUSH <AX, BX, CX, DX, SI, DI, BP, DS, ES>
  1780.                PUSH  CS
  1781.                POP   DS
  1782.                MOV   WORD PTR interrupcion,531Eh  ; opcode PUSH DS,BX
  1783.                MOV   BYTE PTR interrupcion+2,0BBh ; opcode MOV BX,??
  1784.                PUSH  CS
  1785.                POP   ES
  1786.                CALL  inic_general      ; inicializar ciertas variables
  1787.                CALL  pc_ok?
  1788.                TEST  error,0FFFFh
  1789.                JNZ   exit_ins
  1790.                CALL  mx_get_handle     ; obtener entrada Multiplex
  1791.                JNC   handle_ok
  1792.                OR    error,MX64FULL    ; no quedan entradas
  1793.                JMP   exit_ins
  1794. handle_ok:     MOV   multiplex_id,AH   ; entrada multiplex para 2M
  1795.                CALL  preservar_ints    ; tomar nota de vectores
  1796.                MOV   AX,CS
  1797.                LEA   BX,buffer_io
  1798.                CALL  testDMA
  1799.                JNC   dma_ml_ok         ; no hay problemas con el DMA
  1800.                OR    accion,BUFFERPLUS ; sí: aviso al usuario
  1801.                MOV   CX,dma_fix
  1802.                ADD   buffer,CX         ; nuevo buffer
  1803.                MOV   AX,longitud_total
  1804.                ADD   AX,dma_fixp
  1805.                MOV   longitud_total,AX ; nuevo consumo de memoria
  1806. dma_ml_ok:     CALL  activar_ints      ; interceptar vectores
  1807. exit_ins:      CALL  info
  1808.                MOV   BX,pcab_pet_segm
  1809.                MOV   ES,BX
  1810.                MOV   BX,pcab_pet_desp
  1811.                MOV   WORD PTR ES:[BX+3],100h ; indicar retorno correcto
  1812.                MOV   AX,longitud_total
  1813.                MOV   CL,4
  1814.                SHL   AX,CL
  1815.                TEST  error,0FFFFh
  1816.                JZ    exit_ok
  1817.                MOV   WORD PTR ES:[BX+14],0   ; OFFSET 0: no quedará
  1818.                MOV   WORD PTR ES:[BX+16],CS  ; instalado en memoria
  1819.                JMP   exit_interr
  1820. exit_ok:       MOV   WORD PTR ES:[BX+14],AX  ; OFFSET al último byte residente
  1821.                MOV   WORD PTR ES:[BX+16],CS
  1822. exit_interr:   XPOP  <ES, DS, BP, DI, SI, DX, CX, BX, AX>
  1823.                RETF
  1824. main           ENDP
  1825.  
  1826.  
  1827. ;*********************************************************
  1828. ;*                                                       *
  1829. ;*  SUBRUTINAS DE PROPOSITO GENERAL PARA LA INSTALACION  *
  1830. ;*                                                       *
  1831. ;*********************************************************
  1832.  
  1833. ; ------------ Inicializar ciertas variables.
  1834.  
  1835. inic_general   PROC
  1836.                MOV   AX,(bytes_resid+tbuffer+15)/16
  1837.                MOV   longitud_total,AX ; memoria necesaria
  1838.                MOV   segmento_real,CS  ; anotar segmento del bloque
  1839.                MOV   offset_real,0     ; ídem con el offset
  1840.                MOV   DL,0
  1841.                CALL  tipo_disco
  1842.                JC    err_drv
  1843.                AND   DL,DL
  1844.                JZ    err_drv             ; no hay disqueteras
  1845.                MOV   info_A.tipo_drv,AL  ; guardar tipo unidad A:
  1846.                CMP   DL,1
  1847.                JE    fin_inic            ; no existe unidad B:
  1848.                MOV   DL,1
  1849.                CALL  tipo_disco
  1850.                JC    fin_inic
  1851.                MOV   info_B.tipo_drv,AL  ; guardar tipo unidad B:
  1852.                RET
  1853. err_drv:       MOV   AX,MALBIOS
  1854.                OR    error,AX
  1855. fin_inic:      RET
  1856.  
  1857. tipo_disco:    PUSH  ES
  1858.                MOV   AH,8
  1859.                MOV   BL,0
  1860.                INT   13h
  1861.                JC    tipo_dsk_err
  1862.                MOV   AL,BL
  1863.                AND   AL,AL
  1864.                JZ    tipo_dsk_err
  1865.                CMP   AL,4
  1866.                JB    tipo_dsk_ok
  1867.                MOV   AL,4                ; 1.44/2.88 indistinto
  1868. tipo_dsk_ok:   CLC
  1869.                POP   ES
  1870.                RET
  1871. tipo_dsk_err:  STC
  1872.                POP   ES
  1873.                RET
  1874. inic_general   ENDP
  1875.  
  1876. ; ------------ Preservar vectores de interrupción previos.
  1877.  
  1878. preservar_INTs PROC
  1879.                XPUSH <ES, DI>
  1880.                LEA   DI,tabla_vectores
  1881.                MOV   CL,[DI-1]
  1882.                MOV   CH,0              ; CX vectores interceptados
  1883. otro_vector:   XPUSH <CX, DI>
  1884.                MOV   AH,35h
  1885.                MOV   AL,[DI]
  1886.                INT   21h               ; obtener vector de INT xx
  1887.                XPOP  <DI, CX>
  1888.                MOV   [DI+1],BX         ; anotar donde apunta
  1889.                MOV   [DI+3],ES
  1890.                ADD   DI,5
  1891.                LOOP  otro_vector       ; repetir con los restantes
  1892.                XPOP  <DI, ES>
  1893.                RET
  1894. preservar_INTs ENDP
  1895.  
  1896. ; ------------ desviar vectores de interrupción a las nuevas rutinas.
  1897.  
  1898. activar_INTs   PROC
  1899.                LEA   SI,offsets_ints
  1900.                MOV   CX,CS:[SI]        ; CX vectores a desviar
  1901.                ADD   SI,2
  1902. desvia_otro:   MOV   AL,CS:[SI]        ; número del vector en curso
  1903.                MOV   DX,CS:[SI+1]      ; obtener offset
  1904.                MOV   AH,25h
  1905.                INT   21h               ; desviar INT xx a DS:DX
  1906.                ADD   SI,3
  1907.                LOOP  desvia_otro
  1908.                RET
  1909. activar_INTs   ENDP
  1910.  
  1911. ; ------------ Buscar entrada no usada en la interrupción Multiplex.
  1912. ;              A la salida, CF=1 si no hay hueco (ya hay 64 programas
  1913. ;              residentes instalados con esta técnica). Si CF=0, se
  1914. ;              devuelve en AH un valor de entrada libre en la INT 2Fh.
  1915.  
  1916. mx_get_handle  PROC
  1917.                MOV   AH,0C0h
  1918. mx_busca_hndl: PUSH  AX
  1919.                MOV   AL,0
  1920.                INT   2Fh
  1921.                CMP   AL,0FFh
  1922.                POP   AX
  1923.                JNE   mx_si_hueco
  1924.                INC   AH
  1925.                JNZ   mx_busca_hndl
  1926.                STC
  1927.                RET
  1928. mx_si_hueco:   CLC
  1929.                RET
  1930. mx_get_handle  ENDP
  1931.  
  1932. ; ------------ Inicializar variable idioma_sp según idioma del país.
  1933.  
  1934. habla_hispana? PROC
  1935.                XPUSH <AX, BX, CX, DX, BP>
  1936.                MOV   AH,30h
  1937.                INT   21h
  1938.                XCHG  AH,AL             ; AX = versión del DOS
  1939.                MOV   BP,AX
  1940.                MOV   idioma_sp,OFF     ; supuesto de habla no hispana
  1941.                CMP   BP,200h
  1942.                JB    habla_ok
  1943.                LEA   DX,buffer_aux
  1944.                MOV   AX,3800h
  1945.                INT   21h               ; obtener información del pais
  1946.                CMP   BP,20Bh
  1947.                JE    habla_ax          ; DOS 2.11: AX cód. telefónico
  1948.                CMP   BP,300h
  1949.                JB    habla_ok          ; 2.x excepto 2.11: mala suerte
  1950.                MOV   AX,BX
  1951. habla_ax:      LEA   BX,paises_sp-2
  1952.                MOV   CX,numpaises_sp
  1953. habla_sp?:     ADD   BX,2
  1954.                CMP   AX,[BX]
  1955.                JE    habla_hispana
  1956.                LOOP  habla_sp?
  1957. habla_ok:      XPOP  <BP, DX, CX, BX, AX>
  1958.                RET
  1959. habla_hispana: MOV   idioma_sp,ON      ; país de habla hispana
  1960.                XPOP  <BP, DX, CX, BX, AX>
  1961.                RET
  1962. habla_hispana? ENDP
  1963.  
  1964. ; ------------ Imprimir cadena en DS:DX delimitada por un 0 ó un 255.
  1965. ;              Si hay que imprimir en inglés se toma la cadena que va
  1966. ;              después si ésta acaba en 255 (si acaba en 0, no hay
  1967. ;              distinción entre mensaje castellano e inglés).
  1968.  
  1969. print          PROC
  1970.                XPUSH <AX, BX, CX, DX>
  1971. pr_decidir:    CMP   idioma_sp,OFF
  1972.                JE    usar_uk
  1973.                CMP   idioma_sp,ON
  1974.                JE    usar_sp
  1975.                PUSH  DX
  1976.                CALL  habla_hispana?         ; determinar lengua
  1977.                POP   DX
  1978.                JMP   pr_decidir
  1979. usar_uk:       MOV   BX,DX
  1980.                DEC   BX
  1981. usar_uk?:      INC   BX
  1982.                CMP   BYTE PTR [BX],0
  1983.                JE    usar_sp                ; acaba en 0: no traducir
  1984.                CMP   BYTE PTR [BX],255
  1985.                JNE   usar_uk?
  1986.                LEA   DX,[BX+1]              ; acaba en 255: traducir
  1987. usar_sp:       MOV   BX,DX
  1988.                DEC   BX
  1989. print_cad:     INC   BX
  1990.                CMP   BYTE PTR [BX],0
  1991.                JE    prlong_ok
  1992.                CMP   BYTE PTR [BX],255
  1993.                JNE   print_cad              ; calcular longitud
  1994. prlong_ok:     MOV   CX,BX
  1995.                SUB   CX,DX
  1996.                MOV   AH,40h
  1997.                MOV   BX,1
  1998.                INT   21h
  1999.                XPOP  <DX, CX, BX, AX>
  2000.                RET
  2001. print          ENDP
  2002.  
  2003. ; ------------ Informar al usuario.
  2004.  
  2005. info           PROC
  2006.                LEA   DX,instalado_txt
  2007.                TEST  error,0FFFFh
  2008.                JZ    info_ins_ok
  2009.                LEA   DX,noinstall_txt
  2010. info_ins_ok:   CALL  print
  2011.                LEA   DX,err_malpc
  2012.                TEST  error,MALPC
  2013.                JNZ   fin_info
  2014.                LEA   DX,err_maldos
  2015.                TEST  error,MALDOS
  2016.                JNZ   fin_info
  2017.                LEA   DX,err_malbios
  2018.                TEST  error,MALBIOS
  2019.                JNZ   fin_info
  2020.                LEA   DX,err_maldrv
  2021.                TEST  error,MALDRV
  2022.                JNZ   fin_info
  2023.                LEA   DX,err_mx64full
  2024.                TEST  error,MX64FULL
  2025.                JZ    info_war
  2026. fin_info:      CALL  print
  2027. info_war:      TEST  accion,BUFFERPLUS
  2028.                JZ    no_warn
  2029.                LEA   DX,dma_cross_txt
  2030.                CALL  print
  2031. no_warn:       RET
  2032. info           ENDP
  2033.  
  2034. ; ------------ Comprobar que la configuración es la adecuada. Para
  2035. ;              saber si la INT 13h de este ordenador acaba llamando a
  2036. ;              la INT 40h, se desvía la INT 40h y se provoca un inocuo
  2037. ;              reset de disquetes vía INT 13h para comprobar si pasa
  2038. ;              por la INT 40h.
  2039.  
  2040. pc_ok?         PROC
  2041.                CALL  test_i40
  2042.                TEST  accion,I40
  2043.                JZ    test_dos          ; no soportada la INT 40h
  2044.                MOV   nueva_i13,40h
  2045.                MOV   vieja_i13,40h     ; usar INT 40 en vez de INT 13
  2046. test_dos:      MOV   AH,30h
  2047.                INT   21h
  2048.                XCHG  AH,AL
  2049.                CMP   AX,31Eh           ; ¿DOS 3.30 o superior?
  2050.                MOV   AX,MALDOS
  2051.                JB    pc_nok
  2052.                CALL  testAT
  2053.                MOV   AX,MALPC
  2054.                JC    pc_nok
  2055.                TEST  error,MALBIOS
  2056.                JNZ   pc_ok             ; con ese error vale
  2057.                MOV   AX,MALDRV
  2058.                CMP   info_A.tipo_drv,2 ; ¿unidad A: de 1.2?
  2059.                JE    pc_ok
  2060.                CMP   info_A.tipo_drv,4 ; ¿unidad A: de 1.44 ó 2.88?
  2061.                JAE   pc_ok
  2062.                CMP   info_B.tipo_drv,2 ; ¿unidad B: de 1.2?
  2063.                JE    pc_ok
  2064.                CMP   info_B.tipo_drv,4 ; ¿unidad B: de 1.44 ó 2.88?
  2065.                JAE   pc_ok
  2066. pc_nok:        OR    error,AX
  2067. pc_ok:         RET
  2068. pc_ok?         ENDP
  2069.  
  2070.                ; --- Comprobar si la INT 40h está en uso
  2071.  
  2072. test_i40:      XPUSH <DS, ES>          ; *
  2073.                MOV   AX,3540h
  2074.                INT   21h
  2075.                XPUSH <ES, BX>          ; vector de INT 40h original
  2076.                LEA   DX,i40_aux
  2077.                MOV   AX,2540h
  2078.                INT   21h               ; establecer nueva INT 40h
  2079.                XOR   AX,AX
  2080.                MOV   DL,0
  2081.                INT   13h               ; reset de disco
  2082.                XPOP  <DX, DS>
  2083.                MOV   AX,2540h
  2084.                INT   21h               ; restaurar INT 40h original
  2085.                XPOP  <ES, DS>          ; *
  2086.                RET
  2087.  
  2088. i40_aux        PROC
  2089.                OR    CS:accion,I40     ; sí utilizada INT 40h
  2090.                IRET                    ; desde la INT 13h
  2091. i40_aux        ENDP
  2092.  
  2093.                ; --- Detectar 286 ó superior.
  2094.  
  2095. testAT         PROC
  2096.                PUSH  AX
  2097.                PUSHF
  2098.                POP   AX
  2099.                OR    AH,70h        ; intentar activar bit 12, 13 ó 14
  2100.                PUSH  AX            ; del registro de estado
  2101.                POPF
  2102.                PUSHF
  2103.                POP   AX
  2104.                AND   AH,0F0h
  2105.                CMP   AH,0F0h
  2106.                JE    testedAT
  2107.                STC
  2108. testedAT:      CMC                 ; CF = 0 en AT y 1 en PC/XT
  2109.                POP   AX
  2110.                RET
  2111. testAT         ENDP
  2112.  
  2113. ; ------------ Comprobar que el buffer para el DMA en la copia
  2114. ;              residente no cruza una frontera. En ese caso se emplea
  2115. ;              otro buffer ubicado tras el habitual, lo que aumenta el
  2116. ;              consumo de memoria de este área. A la entrada AX apunta
  2117. ;              al segmento que contendrá el buffer y BX el offset. Si
  2118. ;              se produce el cruce, dma_fix devuelve la cuantía en que
  2119. ;              hay que incrementar (en bytes) la dirección base para
  2120. ;              evitarlo y dma_fixp también (pero en párrafos).
  2121.  
  2122. testDMA        PROC
  2123.                XPUSH <AX, CX, DX>
  2124.                MOV   CX,16
  2125.                MUL   CX
  2126.                ADD   AX,BX
  2127.                ADC   DX,0              ; DX:AX = dirección 20 bits
  2128.                MOV   CX,DX
  2129.                PUSH  AX
  2130.                ADD   AX,tbuffer-1      ; buffer para el mayor sector
  2131.                ADC   DX,0
  2132.                POP   AX
  2133.                CMP   DX,CX
  2134.                JE    dmatested
  2135.                NEG   AX
  2136.                MOV   dma_fix,AX
  2137.                ADD   AX,15
  2138.                MOV   CL,4
  2139.                SHR   AX,CL
  2140.                MOV   dma_fixp,AX
  2141.                STC                     ; CF=1 -> cruza frontera
  2142. dmatested:     XPOP  <DX, CX, AX>
  2143.                RET
  2144. testDMA        ENDP
  2145.  
  2146. ; ***********************************************
  2147. ; *                                             *
  2148. ; *   D A T O S    N O    R E S I D E N T E S   *
  2149. ; *                                             *
  2150. ; ***********************************************
  2151.  
  2152. ON             EQU   1         ; constantes booleanas
  2153. OFF            EQU   0
  2154.  
  2155. ; ------------ Gestión de memoria y control de instalación.
  2156.  
  2157. dma_fix        DW    ?         ; bytes para alargar buffer (por DMA)
  2158. dma_fixp       DW    ?         ; párrafos
  2159.  
  2160. offsets_ints   DW    2         ; número de vectores interceptados
  2161. nueva_i13      DB    13h       ; tabla de offsets de los vectores
  2162.                DW    ges_int13 ; de interrupción interceptados
  2163.                DB    2Fh
  2164.                DW    ges_int2F
  2165.  
  2166. MALPC          EQU   1         ; Códigos de error
  2167. MALDOS         EQU   2
  2168. MALBIOS        EQU   4
  2169. MALDRV         EQU   8
  2170. MX64FULL       EQU 128
  2171.  
  2172. BUFFERPLUS     EQU   8         ; códigos de acción e información
  2173. I40            EQU  16
  2174.  
  2175. error          DW    0         ; variable para acumular errores
  2176. accion         DB    0         ; variable que indica lo sucedido
  2177.  
  2178. ; ------------ Códigos de modos y órdenes del DMA y del FDC.
  2179.  
  2180. F_READ         EQU   46h               ; modo DMA para lectura
  2181. F_WRITE        EQU   4Ah               ; modo DMA para escritura
  2182. F_VERIFY       EQU   42h               ; modo DMA para verificación
  2183. F_FORMAT       EQU   01001101b         ; orden de formateo del FDC
  2184.  
  2185. ; ------------ Otras variables.
  2186.  
  2187. idioma_sp      DB    5Ah       ; ni en ON ni en OFF al principio
  2188.  
  2189.                ; --- Código telefónico de países de
  2190.                ;     habla hispana (mucha o poca).
  2191.  
  2192. paises_sp      DW    54                ; Argentina
  2193.                DW    591               ; Bolivia
  2194.                DW    57                ; Colombia
  2195.                DW    506               ; Costa Rica
  2196.                DW    56                ; Chile
  2197.                DW    593               ; Ecuador
  2198.                DW    503               ; El Salvador
  2199.                DW    34                ; España
  2200.                DW    63                ; Filipinas
  2201.                DW    502               ; Guatemala
  2202.                DW    504               ; Honduras
  2203.                DW    212               ; Marruecos
  2204.                DW    52                ; México
  2205.                DW    505               ; Nicaragua
  2206.                DW    507               ; Panamá
  2207.                DW    595               ; Paraguay
  2208.                DW    51                ; Perú
  2209.                DW    80                ; Puerto Rico
  2210.                DW    508               ; República Dominicana
  2211.                DW    598               ; Uruguay
  2212.                DW    58                ; Venezuela
  2213.                DW    3                 ; genérico latinoamérica
  2214. numpaises_sp   EQU   ($-OFFSET paises_sp)/2
  2215.  
  2216. ; ------------ Texto.
  2217.  
  2218. instalado_txt  DB    13,10,"2M 2.1 instalado.",13,10,255
  2219.                DB    13,10,"2M 2.1 installed.",13,10,0
  2220.  
  2221. noinstall_txt  DB    13,10,"2M 2.1 no instalado.",13,10,255
  2222.                DB    13,10,"2M 2.1 not installed.",13,10,0
  2223.  
  2224. err_malpc      DB    "  - Error: Necesario ordenador AT. Utilice 2MX en esta máquina.",13,10,7,255
  2225.                DB    "  - Error: Needs AT system. Use 2MX on this computer.",13,10,7,0
  2226.  
  2227. err_mx64full   DB    "  - Error: Ya hay 64 programas residentes con la misma técnica.",13,10,7,255
  2228.                DB    "  - Error: There are already 64 TSR's with the same technique.",13,10,7,0
  2229.  
  2230. err_maldos     DB    "  - Error: necesaria versión DOS 3.30 ó posterior.",13,10,7,255
  2231.                DB    "  - Error: needs at least DOS 3.30 or above.",13,10,7,0
  2232.  
  2233. err_malbios    DB    "  - Error: No puedo detectar el tipo de las unidades. Instale 2M-ABIOS antes.",13,10,255
  2234.                DB    "  - Error: Impossible to detect drive types. Please install 2M-ABIOS before.",13,10,0
  2235.  
  2236. err_maldrv     DB    "  - Error: Necesaria(s) unidad(es) de alta densidad.",13,10,255
  2237.                DB    "  - Error: Needs high-density floppy drive(s).",13,10,0
  2238.  
  2239. dma_cross_txt  DB    "  - Nota: El buffer de E/S cruzaba una frontera de DMA y fue ampliado.",13,10
  2240.                DB    "          Cambie la ubicación en memoria de 2M para ahorrar unos bytes.",13,10,255
  2241.                DB    "  - Note: I/O buffer has been extended because 2M crosses a DMA boundary.",13,10
  2242.                DB    "          Modify the memory location of 2M to save a little memory.",13,10,0
  2243.  
  2244. buffer_aux     DB    64 DUP (0)   ; buffer para alguna función del DOS
  2245.  
  2246. _PRINCIPAL     ENDS
  2247.                END
  2248.